summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp40
-rw-r--r--ApiDocs.bp6
-rw-r--r--StubLibraries.bp37
-rw-r--r--apct-tests/perftests/core/src/android/view/HandwritingInitiatorPerfTest.java15
-rw-r--r--apex/blobstore/framework/java/android/app/blob/BlobInfo.java1
-rw-r--r--apex/jobscheduler/framework/java/android/app/AlarmManager.java1
-rw-r--r--apex/jobscheduler/framework/java/android/app/job/JobInfo.java1
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java9
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java4
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java11
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java393
-rw-r--r--apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java266
-rw-r--r--apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java142
-rw-r--r--apex/media/framework/java/android/media/MediaSession2Service.java20
-rw-r--r--api/Android.bp2
-rw-r--r--boot/Android.bp4
-rw-r--r--cmds/app_process/Android.bp2
-rw-r--r--cmds/app_process/app_main.cpp5
-rw-r--r--cmds/telecom/src/com/android/commands/telecom/Telecom.java13
-rw-r--r--core/api/current.txt338
-rw-r--r--core/api/module-lib-current.txt112
-rw-r--r--core/api/removed.txt2
-rw-r--r--core/api/system-current.txt334
-rw-r--r--core/api/system-lint-baseline.txt14
-rw-r--r--core/api/test-current.txt23
-rw-r--r--core/java/Android.bp5
-rw-r--r--core/java/android/accessibilityservice/AccessibilityGestureEvent.java2
-rw-r--r--core/java/android/accessibilityservice/AccessibilityServiceInfo.java4
-rw-r--r--core/java/android/accessibilityservice/TouchInteractionController.java22
-rw-r--r--core/java/android/accounts/CantAddAccountActivity.java10
-rw-r--r--core/java/android/accounts/ChooseTypeAndAccountActivity.java10
-rw-r--r--core/java/android/app/Activity.java28
-rw-r--r--core/java/android/app/ActivityManager.java2
-rw-r--r--core/java/android/app/ActivityThread.java11
-rw-r--r--core/java/android/app/AppOpsManager.java6
-rw-r--r--core/java/android/app/Application.java50
-rw-r--r--core/java/android/app/AutomaticZenRule.java8
-rw-r--r--core/java/android/app/ContextImpl.java4
-rw-r--r--core/java/android/app/Dialog.java23
-rw-r--r--core/java/android/app/GameManager.java25
-rw-r--r--core/java/android/app/GameModeInfo.aidl22
-rw-r--r--core/java/android/app/GameModeInfo.java101
-rw-r--r--core/java/android/app/GrantedUriPermission.java2
-rw-r--r--core/java/android/app/IActivityTaskManager.aidl6
-rw-r--r--core/java/android/app/IGameManagerService.aidl4
-rw-r--r--core/java/android/app/IUiModeManager.aidl50
-rw-r--r--core/java/android/app/IWallpaperManager.aidl23
-rw-r--r--core/java/android/app/NotificationChannelGroup.java2
-rw-r--r--core/java/android/app/RemoteInputHistoryItem.java2
-rw-r--r--core/java/android/app/ServiceStartNotAllowedException.java7
-rw-r--r--core/java/android/app/StackTrace.java4
-rw-r--r--core/java/android/app/SystemServiceRegistry.java53
-rw-r--r--core/java/android/app/UiModeManager.java103
-rw-r--r--core/java/android/app/WallpaperColors.java48
-rw-r--r--core/java/android/app/WallpaperManager.java99
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java451
-rw-r--r--core/java/android/app/admin/DevicePolicyResources.java1178
-rw-r--r--core/java/android/app/admin/DevicePolicyStringResource.aidl19
-rw-r--r--core/java/android/app/admin/DevicePolicyStringResource.java145
-rw-r--r--core/java/android/app/admin/IDevicePolicyManager.aidl15
-rw-r--r--core/java/android/app/admin/ParcelableResource.java131
-rw-r--r--core/java/android/app/admin/WifiSsidPolicy.java153
-rw-r--r--core/java/android/app/ambientcontext/AmbientContextEvent.aidl19
-rw-r--r--core/java/android/app/ambientcontext/AmbientContextEvent.java492
-rw-r--r--core/java/android/app/ambientcontext/AmbientContextEventRequest.aidl19
-rw-r--r--core/java/android/app/ambientcontext/AmbientContextEventRequest.java169
-rw-r--r--core/java/android/app/ambientcontext/AmbientContextEventResponse.aidl19
-rw-r--r--core/java/android/app/ambientcontext/AmbientContextEventResponse.java293
-rw-r--r--core/java/android/app/ambientcontext/AmbientContextManager.java147
-rw-r--r--core/java/android/app/ambientcontext/IAmbientContextEventObserver.aidl30
-rw-r--r--core/java/android/app/ambientcontext/OWNERS3
-rw-r--r--core/java/android/app/assist/AssistStructure.java6
-rw-r--r--core/java/android/app/people/ConversationChannel.java8
-rw-r--r--core/java/android/app/people/ConversationStatus.java2
-rw-r--r--core/java/android/app/people/PeopleSpaceTile.java12
-rw-r--r--core/java/android/app/prediction/AppTargetEvent.java2
-rw-r--r--core/java/android/app/servertransaction/ClientTransaction.java4
-rw-r--r--core/java/android/app/smartspace/SmartspaceTargetEvent.java2
-rw-r--r--core/java/android/app/time/ExternalTimeSuggestion.java4
-rw-r--r--core/java/android/app/time/TimeCapabilitiesAndConfig.java4
-rw-r--r--core/java/android/app/time/TimeZoneCapabilitiesAndConfig.java4
-rw-r--r--core/java/android/app/timedetector/GnssTimeSuggestion.java4
-rw-r--r--core/java/android/app/timedetector/ManualTimeSuggestion.java4
-rw-r--r--core/java/android/app/timedetector/NetworkTimeSuggestion.java4
-rw-r--r--core/java/android/app/timedetector/TelephonyTimeSuggestion.java4
-rw-r--r--core/java/android/app/timezone/RulesState.java6
-rw-r--r--core/java/android/app/timezonedetector/ManualTimeZoneSuggestion.java2
-rw-r--r--core/java/android/app/timezonedetector/TelephonyTimeZoneSuggestion.java2
-rw-r--r--core/java/android/app/trust/ITrustListener.aidl5
-rw-r--r--core/java/android/app/trust/ITrustManager.aidl1
-rw-r--r--core/java/android/app/trust/OWNERS1
-rw-r--r--core/java/android/app/trust/TrustManager.java32
-rw-r--r--core/java/android/app/usage/CacheQuotaHint.java2
-rw-r--r--core/java/android/app/usage/UsageStatsManager.java22
-rw-r--r--core/java/android/app/wallpapereffectsgeneration/OWNERS5
-rw-r--r--core/java/android/companion/AssociationRequest.java2
-rw-r--r--core/java/android/companion/BluetoothDeviceFilter.java2
-rw-r--r--core/java/android/companion/BluetoothLeDeviceFilter.java2
-rw-r--r--core/java/android/companion/virtual/IVirtualDevice.aidl2
-rw-r--r--core/java/android/companion/virtual/IVirtualDeviceActivityListener.aidl43
-rw-r--r--core/java/android/companion/virtual/IVirtualDeviceManager.aidl3
-rw-r--r--core/java/android/companion/virtual/VirtualDeviceManager.java192
-rw-r--r--core/java/android/companion/virtual/VirtualDeviceParams.java133
-rw-r--r--core/java/android/content/ContentProvider.java54
-rw-r--r--core/java/android/content/ContentProviderOperation.java2
-rw-r--r--core/java/android/content/Context.java30
-rw-r--r--core/java/android/content/ContextWrapper.java4
-rw-r--r--core/java/android/content/Intent.java52
-rw-r--r--core/java/android/content/PeriodicSync.java2
-rw-r--r--core/java/android/content/SyncInfo.java2
-rw-r--r--core/java/android/content/SyncRequest.java2
-rw-r--r--core/java/android/content/UndoManager.java2
-rw-r--r--core/java/android/content/UriPermission.java2
-rw-r--r--core/java/android/content/om/OverlayManagerTransaction.java2
-rw-r--r--core/java/android/content/pm/AppSearchShortcutInfo.java2
-rw-r--r--core/java/android/content/pm/CrossProfileApps.java22
-rw-r--r--core/java/android/content/pm/InstallSourceInfo.java2
-rw-r--r--core/java/android/content/pm/InstantAppInfo.java2
-rw-r--r--core/java/android/content/pm/InstantAppIntentFilter.java2
-rw-r--r--core/java/android/content/pm/InstantAppResolveInfo.java2
-rw-r--r--core/java/android/content/pm/LauncherActivityInfoInternal.java4
-rw-r--r--core/java/android/content/pm/PackageInstaller.java14
-rw-r--r--core/java/android/content/pm/PackageManager.java8
-rw-r--r--core/java/android/content/pm/PackageParser.java44
-rw-r--r--core/java/android/content/pm/SharedLibraryInfo.java4
-rw-r--r--core/java/android/content/pm/ShortcutInfo.java128
-rw-r--r--core/java/android/content/pm/ShortcutManager.java4
-rw-r--r--core/java/android/content/pm/ShortcutQueryWrapper.java2
-rw-r--r--core/java/android/content/pm/VerifierInfo.java2
-rw-r--r--core/java/android/graphics/fonts/FontUpdateRequest.java6
-rw-r--r--core/java/android/hardware/CameraStreamStats.java12
-rw-r--r--core/java/android/hardware/HardwareBuffer.java12
-rw-r--r--core/java/android/hardware/biometrics/PromptInfo.java2
-rw-r--r--core/java/android/hardware/biometrics/SensorPropertiesInternal.java2
-rw-r--r--core/java/android/hardware/camera2/CameraCaptureSession.java51
-rw-r--r--core/java/android/hardware/camera2/CameraCharacteristics.java110
-rw-r--r--core/java/android/hardware/camera2/CameraDevice.java48
-rw-r--r--core/java/android/hardware/camera2/CameraMetadata.java129
-rw-r--r--core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java9
-rw-r--r--core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java8
-rw-r--r--core/java/android/hardware/camera2/impl/CameraMetadataNative.java39
-rw-r--r--core/java/android/hardware/camera2/params/DynamicRangeProfiles.java301
-rw-r--r--core/java/android/hardware/camera2/params/MandatoryStreamCombination.java205
-rw-r--r--core/java/android/hardware/camera2/params/OutputConfiguration.java53
-rw-r--r--core/java/android/hardware/camera2/params/RecommendedStreamConfigurationMap.java15
-rw-r--r--core/java/android/hardware/display/DisplayManager.java1
-rw-r--r--core/java/android/hardware/face/FaceAuthenticationFrame.java2
-rw-r--r--core/java/android/hardware/face/FaceEnrollFrame.java4
-rw-r--r--core/java/android/hardware/input/IInputManager.aidl4
-rw-r--r--core/java/android/hardware/input/InputDeviceIdentifier.java8
-rw-r--r--core/java/android/hardware/input/InputManager.java106
-rw-r--r--core/java/android/hardware/input/InputManagerInternal.java17
-rw-r--r--core/java/android/hardware/input/VirtualMouse.java23
-rw-r--r--core/java/android/hardware/location/GeofenceHardwareMonitorEvent.java2
-rw-r--r--core/java/android/hardware/usb/IUsbManager.aidl15
-rw-r--r--core/java/android/hardware/usb/IUsbOperationInternal.aidl24
-rw-r--r--core/java/android/hardware/usb/UsbManager.java127
-rw-r--r--core/java/android/hardware/usb/UsbOperationInternal.java131
-rw-r--r--core/java/android/hardware/usb/UsbPort.java322
-rw-r--r--core/java/android/hardware/usb/UsbPortStatus.java214
-rw-r--r--core/java/android/inputmethodservice/IInputMethodWrapper.java20
-rw-r--r--core/java/android/inputmethodservice/InputMethodService.java9
-rw-r--r--core/java/android/inputmethodservice/NavigationBarController.java328
-rw-r--r--core/java/android/inputmethodservice/navigationbar/ButtonDispatcher.java302
-rw-r--r--core/java/android/inputmethodservice/navigationbar/ButtonInterface.java29
-rw-r--r--core/java/android/inputmethodservice/navigationbar/DeadZone.java203
-rw-r--r--core/java/android/inputmethodservice/navigationbar/KeyButtonDrawable.java483
-rw-r--r--core/java/android/inputmethodservice/navigationbar/KeyButtonRipple.java525
-rw-r--r--core/java/android/inputmethodservice/navigationbar/KeyButtonView.java370
-rw-r--r--core/java/android/inputmethodservice/navigationbar/NavigationBarConstants.java62
-rw-r--r--core/java/android/inputmethodservice/navigationbar/NavigationBarFrame.java62
-rw-r--r--core/java/android/inputmethodservice/navigationbar/NavigationBarInflaterView.java429
-rw-r--r--core/java/android/inputmethodservice/navigationbar/NavigationBarUtils.java42
-rw-r--r--core/java/android/inputmethodservice/navigationbar/NavigationBarView.java380
-rw-r--r--core/java/android/inputmethodservice/navigationbar/NavigationHandle.java57
-rw-r--r--core/java/android/inputmethodservice/navigationbar/ReverseLinearLayout.java172
-rw-r--r--core/java/android/net/INetworkPolicyManager.aidl2
-rw-r--r--core/java/android/net/InterfaceConfiguration.java1
-rw-r--r--core/java/android/net/NetworkPolicy.java4
-rw-r--r--core/java/android/net/NetworkPolicyManager.java8
-rw-r--r--core/java/android/net/vcn/VcnConfig.java2
-rw-r--r--core/java/android/net/vcn/VcnGatewayConnectionConfig.java4
-rw-r--r--core/java/android/net/vcn/VcnNetworkPolicyResult.java2
-rw-r--r--core/java/android/net/vcn/VcnTransportInfo.java2
-rw-r--r--core/java/android/net/vcn/VcnUnderlyingNetworkPolicy.java2
-rw-r--r--core/java/android/nfc/BeamShareData.java4
-rw-r--r--core/java/android/os/BatteryConsumer.java1
-rw-r--r--core/java/android/os/BatteryStats.java15
-rw-r--r--core/java/android/os/BatteryStatsManager.java15
-rw-r--r--core/java/android/os/BluetoothBatteryStats.aidl20
-rw-r--r--core/java/android/os/BluetoothBatteryStats.java127
-rw-r--r--core/java/android/os/BluetoothServiceManager.java121
-rw-r--r--core/java/android/os/LocaleList.java32
-rw-r--r--core/java/android/os/Message.java2
-rw-r--r--core/java/android/os/Parcel.java17
-rw-r--r--core/java/android/os/Process.java61
-rw-r--r--core/java/android/os/StrictMode.java2
-rw-r--r--core/java/android/os/SystemVibrator.java314
-rw-r--r--core/java/android/os/TEST_MAPPING9
-rw-r--r--core/java/android/os/Trace.java18
-rw-r--r--core/java/android/os/UserManager.java30
-rw-r--r--core/java/android/os/VibrationAttributes.java20
-rw-r--r--core/java/android/os/VibrationEffect.java2
-rw-r--r--core/java/android/os/Vibrator.java41
-rw-r--r--core/java/android/os/VibratorInfo.java216
-rw-r--r--core/java/android/os/WorkSource.java2
-rw-r--r--core/java/android/os/logcat/ILogcatManagerService.aidl (renamed from packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/ISmartspaceTransitionController.aidl)14
-rw-r--r--core/java/android/os/logcat/OWNERS1
-rw-r--r--core/java/android/os/storage/StorageManager.java52
-rw-r--r--core/java/android/os/storage/StorageVolume.java2
-rw-r--r--core/java/android/os/vibrator/VibratorFrequencyProfile.java100
-rw-r--r--core/java/android/permission/IPermissionController.aidl5
-rw-r--r--core/java/android/permission/IPermissionManager.aidl2
-rw-r--r--core/java/android/permission/PermissionControllerManager.java90
-rw-r--r--core/java/android/permission/PermissionControllerService.java40
-rw-r--r--core/java/android/permission/PermissionManager.java6
-rw-r--r--core/java/android/print/PrintJobInfo.java8
-rw-r--r--core/java/android/print/PrinterId.java2
-rw-r--r--core/java/android/print/PrinterInfo.java6
-rw-r--r--core/java/android/printservice/PrintServiceInfo.java2
-rw-r--r--core/java/android/provider/DeviceConfig.java16
-rw-r--r--core/java/android/provider/Settings.java53
-rw-r--r--core/java/android/service/ambientcontext/AmbientContextDetectionService.java137
-rw-r--r--core/java/android/service/ambientcontext/IAmbientContextDetectionService.aidl31
-rw-r--r--core/java/android/service/ambientcontext/OWNERS3
-rw-r--r--core/java/android/service/autofill/BatchUpdates.java2
-rw-r--r--core/java/android/service/autofill/CompositeUserData.java4
-rw-r--r--core/java/android/service/autofill/CustomDescription.java2
-rw-r--r--core/java/android/service/autofill/Dataset.java12
-rw-r--r--core/java/android/service/autofill/DateTransformation.java4
-rw-r--r--core/java/android/service/autofill/DateValueSanitizer.java2
-rw-r--r--core/java/android/service/autofill/FillRequest.java2
-rw-r--r--core/java/android/service/autofill/FillResponse.java20
-rw-r--r--core/java/android/service/autofill/ImageTransformation.java2
-rw-r--r--core/java/android/service/autofill/NegationValidator.java2
-rw-r--r--core/java/android/service/autofill/RegexValidator.java4
-rw-r--r--core/java/android/service/autofill/SaveInfo.java8
-rw-r--r--core/java/android/service/autofill/TextValueSanitizer.java2
-rw-r--r--core/java/android/service/contentcapture/ActivityEvent.java2
-rw-r--r--core/java/android/service/contentcapture/SnapshotData.java4
-rw-r--r--core/java/android/service/dreams/DreamService.java112
-rw-r--r--core/java/android/service/games/CreateGameSessionResult.aidl23
-rw-r--r--core/java/android/service/games/CreateGameSessionResult.java84
-rw-r--r--core/java/android/service/games/GameScreenshotResult.java181
-rw-r--r--core/java/android/service/games/GameSession.java346
-rw-r--r--core/java/android/service/games/GameSessionService.java54
-rw-r--r--core/java/android/service/games/GameSessionViewHostConfiguration.aidl22
-rw-r--r--core/java/android/service/games/GameSessionViewHostConfiguration.java96
-rw-r--r--core/java/android/service/games/IGameSession.aidl3
-rw-r--r--core/java/android/service/games/IGameSessionController.aidl26
-rw-r--r--core/java/android/service/games/IGameSessionService.aidl6
-rw-r--r--core/java/android/service/notification/Condition.java2
-rw-r--r--core/java/android/service/notification/ConversationChannelWrapper.java4
-rw-r--r--core/java/android/service/notification/NotificationListenerService.java4
-rw-r--r--core/java/android/service/notification/NotificationRankingUpdate.java2
-rw-r--r--core/java/android/service/notification/ZenModeConfig.java12
-rw-r--r--core/java/android/service/notification/ZenPolicy.java4
-rw-r--r--core/java/android/service/persistentdata/PersistentDataBlockManager.java20
-rw-r--r--core/java/android/service/quickaccesswallet/GetWalletCardsResponse.java2
-rw-r--r--core/java/android/service/security/attestationverification/OWNERS1
-rw-r--r--core/java/android/service/settings/suggestions/Suggestion.java4
-rw-r--r--core/java/android/service/smartspace/SmartspaceService.java4
-rw-r--r--core/java/android/service/timezone/TimeZoneProviderEvent.java2
-rw-r--r--core/java/android/service/timezone/TimeZoneProviderSuggestion.java2
-rw-r--r--core/java/android/service/trust/ITrustAgentService.aidl1
-rw-r--r--core/java/android/service/trust/TrustAgentService.java11
-rw-r--r--core/java/android/service/wallpaper/IWallpaperEngine.aidl1
-rw-r--r--core/java/android/service/wallpaper/WallpaperService.java79
-rw-r--r--core/java/android/service/wallpapereffectsgeneration/OWNERS4
-rw-r--r--core/java/android/speech/tts/Voice.java2
-rw-r--r--core/java/android/telephony/SubscriptionPlan.java2
-rw-r--r--core/java/android/text/FontConfig.java6
-rw-r--r--core/java/android/text/PrecomputedText.java4
-rw-r--r--core/java/android/text/style/EasyEditSpan.java2
-rw-r--r--core/java/android/text/style/TextAppearanceSpan.java2
-rw-r--r--core/java/android/util/FeatureFlagUtils.java1
-rw-r--r--core/java/android/util/MemoryIntArray.java2
-rw-r--r--core/java/android/util/SparseDoubleArray.java19
-rw-r--r--core/java/android/util/SparseLongArray.java24
-rw-r--r--core/java/android/view/Choreographer.java2
-rw-r--r--core/java/android/view/Display.java12
-rw-r--r--core/java/android/view/DisplayInfo.java20
-rw-r--r--core/java/android/view/GestureExclusionTracker.java142
-rw-r--r--core/java/android/view/HandwritingInitiator.java36
-rw-r--r--core/java/android/view/HapticFeedbackConstants.java4
-rw-r--r--core/java/android/view/IDisplayWindowListener.aidl8
-rw-r--r--core/java/android/view/IWindowManager.aidl41
-rw-r--r--core/java/android/view/IWindowSession.aidl14
-rw-r--r--core/java/android/view/InputDevice.java17
-rw-r--r--core/java/android/view/KeyboardShortcutInfo.java2
-rw-r--r--core/java/android/view/OnBackInvokedCallback.java70
-rw-r--r--core/java/android/view/OnBackInvokedDispatcher.java76
-rw-r--r--core/java/android/view/OnBackInvokedDispatcherOwner.java33
-rw-r--r--core/java/android/view/SurfaceControl.java9
-rw-r--r--core/java/android/view/SurfaceControlFpsListener.java94
-rw-r--r--core/java/android/view/View.java163
-rw-r--r--core/java/android/view/ViewRootImpl.java69
-rw-r--r--core/java/android/view/ViewRootRectTracker.java165
-rw-r--r--core/java/android/view/WindowManager.java28
-rw-r--r--core/java/android/view/WindowManagerImpl.java20
-rw-r--r--core/java/android/view/WindowlessWindowManager.java10
-rw-r--r--core/java/android/view/accessibility/AccessibilityEvent.java2
-rw-r--r--core/java/android/view/accessibility/AccessibilityWindowInfo.java2
-rw-r--r--core/java/android/view/accessibility/CaptioningManager.java8
-rw-r--r--core/java/android/view/autofill/ParcelableMap.java4
-rw-r--r--core/java/android/view/contentcapture/ContentCaptureCondition.java2
-rw-r--r--core/java/android/view/contentcapture/ContentCaptureContext.java4
-rw-r--r--core/java/android/view/contentcapture/ContentCaptureEvent.java8
-rw-r--r--core/java/android/view/contentcapture/ViewNode.java8
-rw-r--r--core/java/android/view/inputmethod/CursorAnchorInfo.java2
-rw-r--r--core/java/android/view/inputmethod/EditorInfo.java2
-rw-r--r--core/java/android/view/inputmethod/InlineSuggestionsRequest.java2
-rw-r--r--core/java/android/view/inputmethod/InlineSuggestionsResponse.java2
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java2
-rw-r--r--core/java/android/view/textclassifier/ConversationAction.java2
-rw-r--r--core/java/android/view/textclassifier/ConversationActions.java8
-rw-r--r--core/java/android/view/textclassifier/SelectionEvent.java2
-rw-r--r--core/java/android/view/textclassifier/TextClassification.java4
-rw-r--r--core/java/android/view/textclassifier/TextClassificationContext.java2
-rw-r--r--core/java/android/view/textclassifier/TextClassifierEvent.java2
-rw-r--r--core/java/android/view/textclassifier/TextLanguage.java2
-rw-r--r--core/java/android/view/textclassifier/TextLinks.java6
-rw-r--r--core/java/android/view/textclassifier/TextSelection.java6
-rw-r--r--core/java/android/view/translation/TranslationRequest.java4
-rw-r--r--core/java/android/view/translation/TranslationSpec.java2
-rw-r--r--core/java/android/widget/Editor.java72
-rw-r--r--core/java/android/widget/ExpandableListView.java2
-rw-r--r--core/java/android/widget/RemoteViews.java2
-rw-r--r--core/java/android/widget/TextView.java109
-rw-r--r--core/java/android/window/BackNavigationInfo.aidl22
-rw-r--r--core/java/android/window/BackNavigationInfo.java242
-rw-r--r--core/java/android/window/IOnBackInvokedCallback.aidl55
-rw-r--r--core/java/android/window/IOnFpsCallbackListener.aidl30
-rw-r--r--core/java/android/window/TaskFpsCallback.java86
-rw-r--r--core/java/android/window/WindowOnBackInvokedDispatcher.java201
-rw-r--r--core/java/com/android/ims/internal/uce/options/OptionsCapInfo.java2
-rw-r--r--core/java/com/android/ims/internal/uce/options/OptionsCmdStatus.java6
-rw-r--r--core/java/com/android/ims/internal/uce/options/OptionsSipResponse.java2
-rw-r--r--core/java/com/android/ims/internal/uce/presence/PresCapInfo.java2
-rw-r--r--core/java/com/android/ims/internal/uce/presence/PresCmdStatus.java4
-rw-r--r--core/java/com/android/ims/internal/uce/presence/PresResInfo.java2
-rw-r--r--core/java/com/android/ims/internal/uce/presence/PresRlmiInfo.java2
-rw-r--r--core/java/com/android/ims/internal/uce/presence/PresSipResponse.java2
-rw-r--r--core/java/com/android/internal/accessibility/AccessibilityShortcutController.java8
-rw-r--r--core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java27
-rw-r--r--core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java93
-rw-r--r--core/java/com/android/internal/app/IBatteryStats.aidl5
-rw-r--r--core/java/com/android/internal/app/IntentForwarderActivity.java37
-rw-r--r--core/java/com/android/internal/app/ResolverActivity.java87
-rw-r--r--core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java68
-rw-r--r--core/java/com/android/internal/app/UnlaunchableAppActivity.java18
-rw-r--r--core/java/com/android/internal/app/chooser/DisplayResolveInfo.java4
-rw-r--r--core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java6
-rw-r--r--core/java/com/android/internal/graphics/ColorUtils.java5
-rw-r--r--core/java/com/android/internal/logging/UiEventLogger.java8
-rw-r--r--core/java/com/android/internal/logging/UiEventLoggerImpl.java11
-rw-r--r--core/java/com/android/internal/logging/testing/UiEventLoggerFake.java5
-rw-r--r--core/java/com/android/internal/midi/MidiFramer.java6
-rw-r--r--core/java/com/android/internal/midi/OWNERS1
-rw-r--r--core/java/com/android/internal/net/LegacyVpnInfo.java2
-rw-r--r--core/java/com/android/internal/net/VpnConfig.java4
-rw-r--r--core/java/com/android/internal/net/VpnProfile.java4
-rw-r--r--core/java/com/android/internal/notification/SystemNotificationChannels.java11
-rw-r--r--core/java/com/android/internal/os/AppFuseMount.java2
-rw-r--r--core/java/com/android/internal/os/BatteryStatsImpl.java317
-rw-r--r--core/java/com/android/internal/os/BatteryUsageStatsStore.java1
-rw-r--r--core/java/com/android/internal/os/BinderLatencyObserver.java3
-rw-r--r--core/java/com/android/internal/os/BluetoothPowerCalculator.java222
-rw-r--r--core/java/com/android/internal/os/OWNERS1
-rw-r--r--core/java/com/android/internal/os/PowerProfile.java169
-rw-r--r--core/java/com/android/internal/policy/DecorView.java20
-rw-r--r--core/java/com/android/internal/power/ModemPowerProfile.java475
-rw-r--r--core/java/com/android/internal/protolog/ProtoLogGroup.java2
-rw-r--r--core/java/com/android/internal/statusbar/StatusBarIcon.java4
-rw-r--r--core/java/com/android/internal/usb/DumpUtils.java7
-rw-r--r--core/java/com/android/internal/util/FileRotator.java22
-rw-r--r--core/java/com/android/internal/util/ScreenshotHelper.java6
-rw-r--r--core/java/com/android/internal/view/IInputMethod.aidl2
-rw-r--r--core/java/com/android/internal/view/RootViewSurfaceTaker.java5
-rw-r--r--core/java/com/android/internal/widget/LockPatternUtils.java9
-rw-r--r--core/java/com/android/internal/widget/LockPatternView.java40
-rw-r--r--core/java/com/android/internal/widget/SlidingTab.java25
-rw-r--r--core/jni/Android.bp2
-rw-r--r--core/jni/AndroidRuntime.cpp2
-rw-r--r--core/jni/OWNERS3
-rw-r--r--core/jni/android_media_AudioAttributes.cpp10
-rw-r--r--core/jni/android_text_Hyphenator.cpp1
-rw-r--r--core/jni/android_view_SurfaceControl.cpp5
-rw-r--r--core/jni/android_window_WindowInfosListener.cpp20
-rw-r--r--core/proto/OWNERS3
-rw-r--r--core/proto/android/server/vibrator/vibratormanagerservice.proto3
-rw-r--r--core/proto/android/server/windowmanagerservice.proto2
-rw-r--r--core/proto/android/service/netstats.proto30
-rw-r--r--core/proto/android/service/usb.proto13
-rw-r--r--core/res/AndroidManifest.xml52
-rw-r--r--core/res/OWNERS4
-rw-r--r--core/res/res/drawable/ic_ime_nav_back.xml26
-rw-r--r--core/res/res/drawable/ic_ime_switcher.xml25
-rw-r--r--core/res/res/layout/input_method_nav_back.xml28
-rw-r--r--core/res/res/layout/input_method_nav_home_handle.xml25
-rw-r--r--core/res/res/layout/input_method_nav_ime_switcher.xml27
-rw-r--r--core/res/res/layout/input_method_navigation_bar.xml32
-rw-r--r--core/res/res/layout/input_method_navigation_layout.xml57
-rw-r--r--core/res/res/values-sw360dp/dimens.xml4
-rw-r--r--core/res/res/values-sw372dp/dimens.xml20
-rw-r--r--core/res/res/values-sw600dp/dimens.xml5
-rw-r--r--core/res/res/values-sw900dp/dimens.xml8
-rw-r--r--core/res/res/values/attrs.xml30
-rw-r--r--core/res/res/values/attrs_manifest.xml10
-rw-r--r--core/res/res/values/config.xml16
-rw-r--r--core/res/res/values/dimens.xml13
-rw-r--r--core/res/res/values/public.xml7
-rw-r--r--core/res/res/values/strings.xml5
-rw-r--r--core/res/res/values/symbols.xml21
-rw-r--r--core/res/res/xml/power_profile.xml55
-rw-r--r--core/tests/coretests/AndroidManifest.xml2
-rw-r--r--core/tests/coretests/res/xml/power_profile_test.xml123
-rw-r--r--core/tests/coretests/res/xml/power_profile_test_modem.xml151
-rw-r--r--core/tests/coretests/src/android/content/pm/CrossProfileAppsTest.java16
-rw-r--r--core/tests/coretests/src/android/os/VibratorInfoTest.java175
-rw-r--r--core/tests/coretests/src/android/os/VibratorTest.java196
-rw-r--r--core/tests/coretests/src/android/util/SparseDoubleArrayTest.java6
-rw-r--r--core/tests/coretests/src/android/util/SparseLongArrayTest.java12
-rw-r--r--core/tests/coretests/src/android/view/HandwritingInitiatorTest.java39
-rw-r--r--core/tests/coretests/src/android/view/SurfaceControlFpsListenerTest.java46
-rw-r--r--core/tests/coretests/src/android/window/TaskFpsCallbackTest.java66
-rw-r--r--core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java154
-rw-r--r--core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java42
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java43
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java2
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java219
-rw-r--r--core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java10
-rw-r--r--core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java453
-rw-r--r--core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java11
-rw-r--r--data/etc/com.android.settings.xml2
-rw-r--r--data/etc/com.android.systemui.xml3
-rw-r--r--data/etc/privapp-permissions-platform.xml5
-rw-r--r--data/etc/services.core.protolog.json75
-rw-r--r--graphics/java/android/graphics/text/LineBreakConfig.java58
-rw-r--r--graphics/java/android/graphics/text/MeasuredText.java10
-rw-r--r--keystore/java/android/security/KeyStore.java10
-rw-r--r--libs/WindowManager/Shell/res/values-af/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-am/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-ar/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-as/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-az/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-b+sr+Latn/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-be/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-bg/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-bn/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-bs/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-ca/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-cs/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-da/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-de/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-el/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-en-rAU/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-en-rCA/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-en-rGB/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-en-rIN/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-en-rXC/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-es-rUS/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-es/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-et/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-eu/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-fa/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-fi/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-fr-rCA/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-fr/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-gl/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-gu/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-hi/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-hr/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-hu/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-hy/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-in/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-is/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-it/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-iw/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-ja/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-ka/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-kk/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-km/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-kn/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-ko/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-ky/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-lo/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-lt/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-lv/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-mk/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-ml/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-mn/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-mr/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-ms/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-my/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-nb/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-ne/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-nl/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-or/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-pa/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-pl/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-pt-rBR/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-pt-rPT/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-pt/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-ro/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-ru/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-si/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-sk/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-sl/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-sq/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-sr/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-sv/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-sw/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-ta/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-te/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-th/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-tl/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-tr/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-uk/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-ur/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-uz/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-vi/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-zh-rCN/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-zh-rHK/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-zh-rTW/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-zu/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values/config.xml3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java (renamed from services/core/java/com/android/server/wm/BackGestureController.java)22
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java286
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java34
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java21
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java27
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/magnetictarget/MagnetizedObject.kt53
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java25
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUiEventLogger.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java20
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java42
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java50
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/CounterRotatorHelper.java29
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java34
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt8
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/OWNERS2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ExpandBubbleScreen.kt9
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/MultiBubblesScreen.kt9
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OWNERS2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OWNERS2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt8
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt5
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt5
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt5
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/OWNERS2
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java109
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java4
-rw-r--r--libs/androidfw/Locale.cpp43
-rw-r--r--libs/hwui/jni/text/MeasuredText.cpp8
-rw-r--r--libs/hwui/renderthread/RenderThread.cpp2
-rw-r--r--libs/services/Android.bp1
-rw-r--r--location/java/android/location/GnssMeasurement.java2
-rw-r--r--location/java/android/location/GnssMeasurementsEvent.java2
-rw-r--r--location/java/android/location/GpsMeasurementsEvent.java2
-rw-r--r--location/java/android/location/GpsNavigationMessageEvent.java2
-rw-r--r--location/java/android/location/SatellitePvt.java6
-rw-r--r--media/aidl/android/media/audio/common/AudioContentType.aidl4
-rw-r--r--media/aidl/android/media/audio/common/AudioDeviceType.aidl4
-rw-r--r--media/aidl/android/media/audio/common/AudioInputFlags.aidl4
-rw-r--r--media/aidl/android/media/audio/common/AudioOutputFlags.aidl10
-rw-r--r--media/aidl/android/media/audio/common/AudioSource.aidl4
-rw-r--r--media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioContentType.aidl1
-rw-r--r--media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioDeviceType.aidl1
-rw-r--r--media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioInputFlags.aidl1
-rw-r--r--media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioOutputFlags.aidl2
-rw-r--r--media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioSource.aidl1
-rw-r--r--media/java/android/media/AudioAttributes.java46
-rw-r--r--media/java/android/media/AudioDeviceInfo.java14
-rw-r--r--media/java/android/media/AudioDevicePort.java3
-rw-r--r--media/java/android/media/AudioManager.java4
-rw-r--r--media/java/android/media/AudioRecord.java11
-rw-r--r--media/java/android/media/AudioSystem.java15
-rw-r--r--media/java/android/media/ImageWriter.java346
-rw-r--r--media/java/android/media/MediaCodecInfo.java13
-rw-r--r--media/java/android/media/MediaDescription.java6
-rw-r--r--media/java/android/media/MediaFormat.java70
-rw-r--r--media/java/android/media/MediaRecorder.java106
-rw-r--r--media/java/android/media/MediaRoute2Info.java2
-rw-r--r--media/java/android/media/Ringtone.java82
-rw-r--r--media/java/android/media/midi/IMidiManager.aidl4
-rw-r--r--media/java/android/media/midi/MidiDeviceInfo.java140
-rw-r--r--media/java/android/media/midi/MidiDeviceStatus.java2
-rw-r--r--media/java/android/media/midi/MidiManager.java217
-rw-r--r--media/java/android/media/midi/package.html41
-rw-r--r--media/java/android/media/musicrecognition/RecognitionRequest.java4
-rw-r--r--media/java/android/media/session/MediaController.java2
-rw-r--r--media/java/android/media/tv/AitInfo.java8
-rw-r--r--media/java/android/media/tv/DsmccResponse.java184
-rw-r--r--media/java/android/media/tv/StreamEventResponse.java51
-rw-r--r--media/java/android/media/tv/TvContentRatingSystemInfo.java4
-rw-r--r--media/java/android/media/tv/TvInputInfo.java10
-rw-r--r--media/java/android/media/tv/TvInputManager.java10
-rwxr-xr-xmedia/java/android/media/tv/TvInputService.java17
-rw-r--r--media/java/android/media/tv/TvView.java15
-rw-r--r--media/java/android/media/tv/interactive/AppLinkInfo.aidl19
-rw-r--r--media/java/android/media/tv/interactive/AppLinkInfo.java234
-rw-r--r--media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl2
-rw-r--r--media/java/android/media/tv/interactive/ITvInteractiveAppManager.aidl (renamed from media/java/android/media/tv/interactive/ITvIAppManager.aidl)7
-rw-r--r--media/java/android/media/tv/interactive/ITvInteractiveAppManagerCallback.aidl2
-rw-r--r--media/java/android/media/tv/interactive/ITvInteractiveAppService.aidl7
-rw-r--r--media/java/android/media/tv/interactive/ITvInteractiveAppServiceCallback.aidl6
-rw-r--r--media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl4
-rw-r--r--media/java/android/media/tv/interactive/TvInteractiveAppInfo.java13
-rwxr-xr-xmedia/java/android/media/tv/interactive/TvInteractiveAppManager.java (renamed from media/java/android/media/tv/interactive/TvIAppManager.java)264
-rwxr-xr-xmedia/java/android/media/tv/interactive/TvInteractiveAppService.java (renamed from media/java/android/media/tv/interactive/TvIAppService.java)122
-rwxr-xr-xmedia/java/android/media/tv/interactive/TvInteractiveAppView.java71
-rw-r--r--media/java/android/media/tv/tuner/Tuner.java32
-rw-r--r--media/java/android/media/tv/tuner/filter/SectionSettings.java2
-rw-r--r--media/java/android/media/tv/tuner/frontend/FrontendStatus.java31
-rw-r--r--media/jni/android_media_ImageWriter.cpp33
-rw-r--r--media/jni/android_media_tv_Tuner.cpp34
-rw-r--r--media/jni/android_media_tv_Tuner.h1
-rw-r--r--media/jni/tuner/FrontendClient.cpp9
-rw-r--r--media/jni/tuner/FrontendClient.h5
-rw-r--r--media/native/midi/MidiDeviceInfo.cpp5
-rw-r--r--media/native/midi/MidiDeviceInfo.h14
-rw-r--r--media/native/midi/amidi.cpp8
-rw-r--r--media/native/midi/amidi_internal.h1
-rw-r--r--media/native/midi/include/amidi/AMidi.h96
-rw-r--r--media/native/midi/libamidi.map.txt1
-rw-r--r--media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java20
-rw-r--r--native/android/choreographer.cpp2
-rw-r--r--native/android/surface_control.cpp2
-rw-r--r--packages/CompanionDeviceManager/res/layout/data_transfer_confirmation.xml73
-rw-r--r--packages/CompanionDeviceManager/res/values/strings.xml11
-rw-r--r--packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDataTransferActivity.java113
-rw-r--r--packages/ConnectivityT/framework-t/Android.bp5
-rw-r--r--packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStats.java16
-rw-r--r--packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java200
-rw-r--r--packages/ConnectivityT/framework-t/src/android/net/DataUsageRequest.java2
-rw-r--r--packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkSpecifier.java6
-rw-r--r--packages/ConnectivityT/framework-t/src/android/net/IInternalNetworkManagementListener.aidl (renamed from core/java/android/net/IInternalNetworkManagementListener.aidl)0
-rw-r--r--packages/ConnectivityT/framework-t/src/android/net/INetworkStatsService.aidl10
-rw-r--r--packages/ConnectivityT/framework-t/src/android/net/INetworkStatsSession.aidl5
-rw-r--r--packages/ConnectivityT/framework-t/src/android/net/InternalNetworkManagementException.aidl (renamed from core/java/android/net/InternalNetworkManagementException.aidl)0
-rw-r--r--packages/ConnectivityT/framework-t/src/android/net/InternalNetworkManagementException.java (renamed from core/java/android/net/InternalNetworkManagementException.java)0
-rw-r--r--packages/ConnectivityT/framework-t/src/android/net/InternalNetworkUpdateRequest.aidl (renamed from core/java/android/net/InternalNetworkUpdateRequest.aidl)0
-rw-r--r--packages/ConnectivityT/framework-t/src/android/net/InternalNetworkUpdateRequest.java (renamed from core/java/android/net/InternalNetworkUpdateRequest.java)0
-rw-r--r--packages/ConnectivityT/framework-t/src/android/net/IpSecConfig.java8
-rw-r--r--packages/ConnectivityT/framework-t/src/android/net/IpSecManager.java24
-rw-r--r--packages/ConnectivityT/framework-t/src/android/net/IpSecTransform.java24
-rw-r--r--packages/ConnectivityT/framework-t/src/android/net/IpSecUdpEncapResponse.java2
-rw-r--r--packages/ConnectivityT/framework-t/src/android/net/NetworkIdentity.java418
-rw-r--r--packages/ConnectivityT/framework-t/src/android/net/NetworkIdentitySet.java70
-rw-r--r--packages/ConnectivityT/framework-t/src/android/net/NetworkStateSnapshot.java6
-rw-r--r--packages/ConnectivityT/framework-t/src/android/net/NetworkStats.java32
-rw-r--r--packages/ConnectivityT/framework-t/src/android/net/NetworkStatsCollection.java92
-rw-r--r--packages/ConnectivityT/framework-t/src/android/net/NetworkStatsHistory.java286
-rw-r--r--packages/ConnectivityT/framework-t/src/android/net/NetworkTemplate.java42
-rw-r--r--packages/ConnectivityT/framework-t/src/android/net/TrafficStats.java42
-rw-r--r--packages/ConnectivityT/framework-t/src/android/net/UnderlyingNetworkInfo.java2
-rw-r--r--packages/ConnectivityT/service/Android.bp28
-rw-r--r--packages/ConnectivityT/service/jni/onload.cpp38
-rw-r--r--packages/ConnectivityT/service/src/com/android/server/net/BpfInterfaceMapUpdater.java139
-rw-r--r--packages/ConnectivityT/service/src/com/android/server/net/DelayedDiskWrite.java (renamed from services/core/java/com/android/server/net/DelayedDiskWrite.java)22
-rw-r--r--packages/ConnectivityT/service/src/com/android/server/net/InterfaceMapValue.java35
-rw-r--r--packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java242
-rw-r--r--packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java3
-rw-r--r--packages/SettingsLib/res/values/config.xml5
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/applications/InterestingConfigChanges.java5
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java6
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java112
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java62
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java4
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java11
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ConnectivityPreferenceControllerTest.java2
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/dream/DreamBackendTest.java96
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminControllerTest.java5
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java2
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java75
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/AccessPointPreferenceTest.java8
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java3
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java2
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java3
-rw-r--r--packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java1
-rw-r--r--packages/Shell/AndroidManifest.xml9
-rw-r--r--packages/SystemUI/AndroidManifest.xml3
-rw-r--r--packages/SystemUI/OWNERS4
-rw-r--r--packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java5
-rw-r--r--packages/SystemUI/res-keyguard/layout/footer_actions.xml15
-rw-r--r--packages/SystemUI/res-keyguard/values/bools.xml1
-rw-r--r--packages/SystemUI/res/drawable/ic_warning.xml19
-rw-r--r--packages/SystemUI/res/drawable/qs_customizer_background_transition.xml2
-rw-r--r--packages/SystemUI/res/drawable/qs_detail_background.xml2
-rw-r--r--packages/SystemUI/res/layout/media_ttt_chip.xml23
-rw-r--r--packages/SystemUI/res/layout/qs_footer_impl.xml18
-rw-r--r--packages/SystemUI/res/values-af/strings.xml14
-rw-r--r--packages/SystemUI/res/values-af/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-am/strings.xml15
-rw-r--r--packages/SystemUI/res/values-am/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-ar/strings.xml16
-rw-r--r--packages/SystemUI/res/values-ar/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-as/strings.xml16
-rw-r--r--packages/SystemUI/res/values-as/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-az/strings.xml15
-rw-r--r--packages/SystemUI/res/values-az/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-b+sr+Latn/strings.xml14
-rw-r--r--packages/SystemUI/res/values-b+sr+Latn/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-be/strings.xml20
-rw-r--r--packages/SystemUI/res/values-be/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-bg/strings.xml16
-rw-r--r--packages/SystemUI/res/values-bg/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-bn/strings.xml15
-rw-r--r--packages/SystemUI/res/values-bn/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-bs/strings.xml14
-rw-r--r--packages/SystemUI/res/values-bs/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-ca/strings.xml16
-rw-r--r--packages/SystemUI/res/values-ca/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-cs/strings.xml15
-rw-r--r--packages/SystemUI/res/values-cs/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-da/strings.xml16
-rw-r--r--packages/SystemUI/res/values-da/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-de/strings.xml15
-rw-r--r--packages/SystemUI/res/values-de/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-el/strings.xml15
-rw-r--r--packages/SystemUI/res/values-el/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-en-rAU/strings.xml11
-rw-r--r--packages/SystemUI/res/values-en-rCA/strings.xml11
-rw-r--r--packages/SystemUI/res/values-en-rGB/strings.xml11
-rw-r--r--packages/SystemUI/res/values-en-rIN/strings.xml11
-rw-r--r--packages/SystemUI/res/values-en-rXC/strings.xml8
-rw-r--r--packages/SystemUI/res/values-es-rUS/strings.xml15
-rw-r--r--packages/SystemUI/res/values-es-rUS/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-es/strings.xml16
-rw-r--r--packages/SystemUI/res/values-es/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-et/strings.xml16
-rw-r--r--packages/SystemUI/res/values-et/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-eu/strings.xml15
-rw-r--r--packages/SystemUI/res/values-eu/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-fa/strings.xml16
-rw-r--r--packages/SystemUI/res/values-fa/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-fi/strings.xml18
-rw-r--r--packages/SystemUI/res/values-fi/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-fr-rCA/strings.xml16
-rw-r--r--packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-fr/strings.xml15
-rw-r--r--packages/SystemUI/res/values-fr/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-gl/strings.xml16
-rw-r--r--packages/SystemUI/res/values-gl/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-gu/strings.xml15
-rw-r--r--packages/SystemUI/res/values-gu/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-hi/strings.xml16
-rw-r--r--packages/SystemUI/res/values-hi/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-hr/strings.xml16
-rw-r--r--packages/SystemUI/res/values-hr/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-hu/strings.xml16
-rw-r--r--packages/SystemUI/res/values-hu/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-hy/strings.xml15
-rw-r--r--packages/SystemUI/res/values-hy/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-in/strings.xml16
-rw-r--r--packages/SystemUI/res/values-in/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-is/strings.xml16
-rw-r--r--packages/SystemUI/res/values-is/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-it/strings.xml15
-rw-r--r--packages/SystemUI/res/values-it/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-iw/strings.xml16
-rw-r--r--packages/SystemUI/res/values-iw/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-ja/strings.xml14
-rw-r--r--packages/SystemUI/res/values-ja/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-ka/strings.xml14
-rw-r--r--packages/SystemUI/res/values-ka/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-kk/strings.xml15
-rw-r--r--packages/SystemUI/res/values-kk/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-km/strings.xml14
-rw-r--r--packages/SystemUI/res/values-km/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-kn/strings.xml16
-rw-r--r--packages/SystemUI/res/values-kn/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-ko/strings.xml16
-rw-r--r--packages/SystemUI/res/values-ko/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-ky/strings.xml15
-rw-r--r--packages/SystemUI/res/values-ky/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-lo/strings.xml15
-rw-r--r--packages/SystemUI/res/values-lo/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-lt/strings.xml15
-rw-r--r--packages/SystemUI/res/values-lt/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-lv/strings.xml16
-rw-r--r--packages/SystemUI/res/values-lv/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-mk/strings.xml14
-rw-r--r--packages/SystemUI/res/values-mk/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-ml/strings.xml14
-rw-r--r--packages/SystemUI/res/values-ml/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-mn/strings.xml14
-rw-r--r--packages/SystemUI/res/values-mn/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-mr/strings.xml14
-rw-r--r--packages/SystemUI/res/values-mr/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-ms/strings.xml15
-rw-r--r--packages/SystemUI/res/values-ms/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-my/strings.xml16
-rw-r--r--packages/SystemUI/res/values-my/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-nb/strings.xml16
-rw-r--r--packages/SystemUI/res/values-nb/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-ne/strings.xml14
-rw-r--r--packages/SystemUI/res/values-ne/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-nl/strings.xml15
-rw-r--r--packages/SystemUI/res/values-nl/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-or/strings.xml15
-rw-r--r--packages/SystemUI/res/values-or/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-pa/strings.xml15
-rw-r--r--packages/SystemUI/res/values-pa/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-pl/strings.xml16
-rw-r--r--packages/SystemUI/res/values-pl/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-pt-rBR/strings.xml12
-rw-r--r--packages/SystemUI/res/values-pt-rPT/strings.xml11
-rw-r--r--packages/SystemUI/res/values-pt/strings.xml12
-rw-r--r--packages/SystemUI/res/values-ro/strings.xml15
-rw-r--r--packages/SystemUI/res/values-ro/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-ru/strings.xml14
-rw-r--r--packages/SystemUI/res/values-ru/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-si/strings.xml16
-rw-r--r--packages/SystemUI/res/values-si/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-sk/strings.xml12
-rw-r--r--packages/SystemUI/res/values-sl/strings.xml12
-rw-r--r--packages/SystemUI/res/values-sq/strings.xml16
-rw-r--r--packages/SystemUI/res/values-sq/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-sr/strings.xml14
-rw-r--r--packages/SystemUI/res/values-sr/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-sv/strings.xml15
-rw-r--r--packages/SystemUI/res/values-sv/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-sw/strings.xml16
-rw-r--r--packages/SystemUI/res/values-sw/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-ta/strings.xml14
-rw-r--r--packages/SystemUI/res/values-ta/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-te/strings.xml14
-rw-r--r--packages/SystemUI/res/values-te/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-th/strings.xml14
-rw-r--r--packages/SystemUI/res/values-th/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-tl/strings.xml16
-rw-r--r--packages/SystemUI/res/values-tl/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-tr/strings.xml16
-rw-r--r--packages/SystemUI/res/values-tr/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-uk/strings.xml16
-rw-r--r--packages/SystemUI/res/values-uk/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-ur/strings.xml14
-rw-r--r--packages/SystemUI/res/values-ur/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-uz/strings.xml13
-rw-r--r--packages/SystemUI/res/values-vi/strings.xml16
-rw-r--r--packages/SystemUI/res/values-vi/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-zh-rCN/strings.xml14
-rw-r--r--packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-zh-rHK/strings.xml14
-rw-r--r--packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-zh-rTW/strings.xml15
-rw-r--r--packages/SystemUI/res/values-zh-rTW/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-zu/strings.xml15
-rw-r--r--packages/SystemUI/res/values-zu/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values/colors.xml1
-rw-r--r--packages/SystemUI/res/values/config.xml16
-rw-r--r--packages/SystemUI/res/values/dimens.xml5
-rw-r--r--packages/SystemUI/res/values/strings.xml12
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/IDeviceSenderCallback.aidl46
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/IDeviceSenderService.aidl133
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/IUndoTransferCallback.aidl32
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/navigationbar/RegionSamplingHelper.java34
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java33
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java4
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java78
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/ILauncherUnlockAnimationController.aidl44
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/ISmartspaceCallback.aidl34
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/ISysuiUnlockAnimationController.aidl33
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/SmartspaceState.kt7
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionProgressProvider.kt4
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfig.kt24
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/unfold/progress/FixedTimingTransitionProgressProvider.kt34
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt32
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt63
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/unfold/updates/FoldStateProvider.kt20
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/EmptyHingeAngleProvider.kt12
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/HingeAngleProvider.kt7
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/HingeSensorAngleProvider.kt8
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/unfold/updates/screen/ScreenStatusProvider.kt2
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProvider.kt11
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt15
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProvider.kt26
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java13
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java1
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java49
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardConstants.java4
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java4
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java2
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java4
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java54
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java7
-rw-r--r--packages/SystemUI/src/com/android/keyguard/NumPadKey.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIFactory.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java27
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java242
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/ComplicationProvider.java49
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/appwidgets/AppWidgetProvider.java101
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/appwidgets/ComplicationPrimer.java120
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/appwidgets/ComplicationProvider.java83
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/appwidgets/dagger/AppWidgetComponent.java39
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/Flags.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt712
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt192
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt198
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt63
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderService.kt120
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerController.java40
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/FooterActionsControllerBuilder.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/FooterActionsView.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java28
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/shared/system/smartspace/SmartspaceTransitionController.kt143
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java39
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt82
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/SectionHeaderVisibilityProvider.kt34
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerLogger.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java259
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt200
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/BindEventManager.kt45
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/BindEventManagerImpl.kt42
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifComparator.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionListener.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifEvent.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/DebugModeFilterProvider.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeController.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RootNodeController.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/SectionHeaderController.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java120
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenOffAnimationController.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java49
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java87
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt27
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingsManager.java71
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java85
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt50
-rw-r--r--packages/SystemUI/src/com/android/systemui/unfold/UnfoldLatencyTracker.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt80
-rw-r--r--packages/SystemUI/src/com/android/systemui/unfold/UnfoldProgressProvider.kt28
-rw-r--r--packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionWallpaperController.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java39
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java31
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java91
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/ComplicationProviderTest.java66
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/appwidgets/ComplicationPrimerTest.java191
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/appwidgets/ComplicationProviderTest.java137
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt146
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt275
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderServiceTest.kt92
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerControllerTest.java57
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java42
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt34
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java13
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java34
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java35
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt48
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.java274
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt238
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt37
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferTest.java12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt80
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java112
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java69
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardStateController.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java54
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java54
-rw-r--r--proto/src/camera.proto3
-rw-r--r--services/Android.bp4
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java1
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java29
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java18
-rw-r--r--services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java11
-rw-r--r--services/companion/java/com/android/server/companion/AssociationStore.java20
-rw-r--r--services/companion/java/com/android/server/companion/AssociationStoreImpl.java29
-rw-r--r--services/companion/java/com/android/server/companion/CompanionApplicationController.java317
-rw-r--r--services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java86
-rw-r--r--services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java11
-rw-r--r--services/companion/java/com/android/server/companion/DataStoreUtils.java85
-rw-r--r--services/companion/java/com/android/server/companion/PersistentDataStore.java35
-rw-r--r--services/companion/java/com/android/server/companion/SystemDataTransferRequestDataStore.java226
-rw-r--r--services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java436
-rw-r--r--services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java166
-rw-r--r--services/companion/java/com/android/server/companion/presence/Utils.java49
-rw-r--r--services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java60
-rw-r--r--services/companion/java/com/android/server/companion/virtual/InputController.java254
-rw-r--r--services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java73
-rw-r--r--services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java6
-rw-r--r--services/core/Android.bp3
-rw-r--r--services/core/java/android/app/usage/OWNERS1
-rw-r--r--services/core/java/android/app/usage/UsageStatsManagerInternal.java50
-rw-r--r--services/core/java/android/content/pm/PackageManagerInternal.java35
-rw-r--r--services/core/java/com/android/server/BatteryService.java46
-rw-r--r--services/core/java/com/android/server/MasterClearReceiver.java11
-rw-r--r--services/core/java/com/android/server/StorageManagerService.java56
-rw-r--r--services/core/java/com/android/server/UiModeManagerService.java87
-rw-r--r--services/core/java/com/android/server/am/ActiveServices.java19
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java68
-rw-r--r--services/core/java/com/android/server/am/BatteryStatsService.java25
-rw-r--r--services/core/java/com/android/server/am/BroadcastQueue.java48
-rw-r--r--services/core/java/com/android/server/am/CachedAppOptimizer.java2
-rw-r--r--services/core/java/com/android/server/am/ContentProviderHelper.java37
-rw-r--r--services/core/java/com/android/server/am/OomAdjuster.java2
-rw-r--r--services/core/java/com/android/server/am/ProcessRecord.java2
-rw-r--r--services/core/java/com/android/server/am/ServiceRecord.java35
-rw-r--r--services/core/java/com/android/server/am/UserController.java2
-rw-r--r--services/core/java/com/android/server/ambientcontext/AmbientContextManagerPerUserService.java294
-rw-r--r--services/core/java/com/android/server/ambientcontext/AmbientContextManagerService.java220
-rw-r--r--services/core/java/com/android/server/ambientcontext/AmbientContextShellCommand.java164
-rw-r--r--services/core/java/com/android/server/ambientcontext/OWNERS3
-rw-r--r--services/core/java/com/android/server/ambientcontext/RemoteAmbientContextDetectionService.java74
-rw-r--r--services/core/java/com/android/server/app/GameManagerService.java523
-rw-r--r--services/core/java/com/android/server/app/GameManagerShellCommand.java256
-rw-r--r--services/core/java/com/android/server/app/GameServiceProviderInstanceFactoryImpl.java6
-rw-r--r--services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java338
-rw-r--r--services/core/java/com/android/server/app/GameSessionRecord.java70
-rw-r--r--services/core/java/com/android/server/audio/AudioDeviceInventory.java3
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java6
-rw-r--r--services/core/java/com/android/server/biometrics/log/BiometricFrameworkStatsLogger.java126
-rw-r--r--services/core/java/com/android/server/biometrics/log/BiometricLogger.java (renamed from services/core/java/com/android/server/biometrics/sensors/LoggableMonitor.java)186
-rw-r--r--services/core/java/com/android/server/biometrics/log/CallbackWithProbe.java56
-rw-r--r--services/core/java/com/android/server/biometrics/log/Probe.java30
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java8
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java8
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java123
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java6
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java30
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallback.java43
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/ClientMonitorCompositeCallback.java53
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/EnrollClient.java8
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/EnrollmentModifier.java2
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java2
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java2
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java14
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java9
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/Interruptable.java6
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/InvalidationClient.java2
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/InvalidationRequesterClient.java2
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/RemovalClient.java3
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java2
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java2
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java4
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java3
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java15
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java3
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java14
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java3
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetFeatureClient.java3
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java7
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java3
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceSetFeatureClient.java3
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStartUserClient.java3
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStopUserClient.java3
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java3
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java15
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java9
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java7
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClient.java5
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGetFeatureClient.java3
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceResetLockoutClient.java3
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceSetFeatureClient.java3
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceUpdateActiveUserClient.java3
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintStateCallback.java3
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java4
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java3
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java18
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java3
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java9
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java3
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java10
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java3
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintStartUserClient.java3
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintStopUserClient.java3
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java3
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java12
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java5
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java12
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java7
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java9
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintResetLockoutClient.java3
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java3
-rw-r--r--services/core/java/com/android/server/camera/CameraServiceProxy.java48
-rw-r--r--services/core/java/com/android/server/clipboard/ClipboardService.java11
-rw-r--r--services/core/java/com/android/server/display/DisplayDeviceInfo.java11
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerController.java122
-rw-r--r--services/core/java/com/android/server/display/LocalDisplayAdapter.java1
-rw-r--r--services/core/java/com/android/server/display/LogicalDisplay.java1
-rw-r--r--services/core/java/com/android/server/display/LogicalDisplayMapper.java12
-rw-r--r--services/core/java/com/android/server/input/InputManagerService.java65
-rw-r--r--services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java224
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodBindingController.java10
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java451
-rw-r--r--services/core/java/com/android/server/location/gnss/GnssLocationProvider.java149
-rw-r--r--services/core/java/com/android/server/location/gnss/hal/GnssNative.java28
-rw-r--r--services/core/java/com/android/server/locksettings/LockSettingsService.java31
-rw-r--r--services/core/java/com/android/server/logcat/LogcatManagerService.java118
-rw-r--r--services/core/java/com/android/server/logcat/OWNERS5
-rw-r--r--services/core/java/com/android/server/media/BluetoothRouteProvider.java11
-rw-r--r--services/core/java/com/android/server/net/NetworkPolicyManagerService.java53
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java14
-rw-r--r--services/core/java/com/android/server/notification/PreferencesHelper.java14
-rw-r--r--services/core/java/com/android/server/om/IdmapDaemon.java119
-rw-r--r--services/core/java/com/android/server/pm/ApexManager.java5
-rw-r--r--services/core/java/com/android/server/pm/AppDataHelper.java48
-rw-r--r--services/core/java/com/android/server/pm/ComputerEngine.java37
-rw-r--r--services/core/java/com/android/server/pm/DeletePackageHelper.java3
-rw-r--r--services/core/java/com/android/server/pm/InstallPackageHelper.java11
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerService.java11
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java18
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java477
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java2
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerServiceUtils.java6
-rw-r--r--services/core/java/com/android/server/pm/RemovePackageHelper.java5
-rw-r--r--services/core/java/com/android/server/pm/ShortcutPackage.java18
-rw-r--r--services/core/java/com/android/server/pm/ShortcutParser.java3
-rw-r--r--services/core/java/com/android/server/pm/ShortcutService.java4
-rw-r--r--services/core/java/com/android/server/pm/SuspendPackageHelper.java611
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java15
-rw-r--r--services/core/java/com/android/server/pm/UserRestrictionsUtils.java1
-rw-r--r--services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java28
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerService.java14
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java15
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java5
-rw-r--r--services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java10
-rw-r--r--services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageImpl.java15
-rw-r--r--services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageRead.java5
-rw-r--r--services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java4
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java16
-rw-r--r--services/core/java/com/android/server/stats/pull/StatsPullAtomService.java32
-rw-r--r--services/core/java/com/android/server/statusbar/StatusBarManagerService.java46
-rw-r--r--services/core/java/com/android/server/storage/DeviceStorageMonitorService.java10
-rw-r--r--services/core/java/com/android/server/trust/TrustAgentWrapper.java36
-rw-r--r--services/core/java/com/android/server/trust/TrustManagerService.java46
-rw-r--r--services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java (renamed from services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java)74
-rw-r--r--services/core/java/com/android/server/uri/UriGrantsManagerService.java46
-rw-r--r--services/core/java/com/android/server/uri/UriMetricsHelper.java101
-rw-r--r--services/core/java/com/android/server/vcn/Vcn.java69
-rw-r--r--services/core/java/com/android/server/vibrator/ClippingAmplitudeAndFrequencyAdapter.java18
-rw-r--r--services/core/java/com/android/server/vibrator/RampToStepAdapter.java2
-rw-r--r--services/core/java/com/android/server/vibrator/StepToRampAdapter.java2
-rw-r--r--services/core/java/com/android/server/vibrator/VibrationScaler.java18
-rw-r--r--services/core/java/com/android/server/vibrator/VibrationSettings.java31
-rw-r--r--services/core/java/com/android/server/vibrator/VibratorManagerService.java9
-rw-r--r--services/core/java/com/android/server/wallpaper/WallpaperManagerService.java188
-rw-r--r--services/core/java/com/android/server/wallpaper/WallpaperManagerShellCommand.java95
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java36
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecordInputSink.java4
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java21
-rw-r--r--services/core/java/com/android/server/wm/BackNavigationController.java240
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java60
-rw-r--r--services/core/java/com/android/server/wm/DisplayPolicy.java151
-rw-r--r--services/core/java/com/android/server/wm/DisplayWindowListenerController.java15
-rw-r--r--services/core/java/com/android/server/wm/EmbeddedWindowController.java52
-rw-r--r--services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java2
-rw-r--r--services/core/java/com/android/server/wm/InputManagerCallback.java6
-rw-r--r--services/core/java/com/android/server/wm/InputTarget.java11
-rw-r--r--services/core/java/com/android/server/wm/InsetsStateController.java1
-rw-r--r--services/core/java/com/android/server/wm/OverlayHost.java2
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java12
-rw-r--r--services/core/java/com/android/server/wm/Session.java17
-rw-r--r--services/core/java/com/android/server/wm/Task.java6
-rw-r--r--services/core/java/com/android/server/wm/TaskFpsCallbackController.java69
-rw-r--r--services/core/java/com/android/server/wm/TaskFragment.java95
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerInternal.java22
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java272
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java147
-rw-r--r--services/core/jni/Android.bp16
-rw-r--r--services/core/jni/com_android_server_app_GameManagerService.cpp44
-rw-r--r--services/core/jni/com_android_server_input_InputManagerService.cpp36
-rw-r--r--services/core/jni/com_android_server_location_GnssLocationProvider.cpp130
-rw-r--r--services/core/jni/com_android_server_vibrator_VibratorController.cpp26
-rw-r--r--services/core/jni/com_android_server_wm_TaskFpsCallbackController.cpp (renamed from core/jni/android_view_SurfaceControlFpsListener.cpp)64
-rw-r--r--services/core/jni/gnss/AGnssRil.cpp172
-rw-r--r--services/core/jni/gnss/AGnssRil.h91
-rw-r--r--services/core/jni/gnss/AGnssRilCallback.cpp69
-rw-r--r--services/core/jni/gnss/AGnssRilCallback.h90
-rw-r--r--services/core/jni/gnss/Android.bp2
-rw-r--r--services/core/jni/gnss/GnssBatching.cpp15
-rw-r--r--services/core/jni/gnss/GnssBatching.h7
-rw-r--r--services/core/jni/gnss/GnssMeasurementCallback.cpp1
-rw-r--r--services/core/jni/onload.cpp4
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java40
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java12
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DeviceManagementResourcesProvider.java123
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java350
-rw-r--r--services/java/com/android/server/SystemServer.java45
-rw-r--r--services/midi/java/com/android/server/midi/MidiService.java127
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt5
-rw-r--r--services/tests/mockingservicestests/Android.bp2
-rw-r--r--services/tests/mockingservicestests/jni/Android.bp14
-rw-r--r--services/tests/mockingservicestests/jni/onload.cpp2
-rw-r--r--services/tests/mockingservicestests/src/android/service/games/GameSessionTest.java391
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java332
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java855
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java4
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java561
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/gnss/hal/FakeGnssHal.java18
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt2
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt507
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackagesBroadcastTest.kt184
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java12
-rw-r--r--services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java15
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java255
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/AcquisitionClientTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/BaseClientMonitorTest.java130
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java38
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java16
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/CompositeCallbackTest.java54
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClientTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java84
-rw-r--r--services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java109
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java156
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java24
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java18
-rw-r--r--services/tests/servicestests/src/com/android/server/statusbar/StatusBarManagerServiceTest.java42
-rw-r--r--services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java64
-rw-r--r--services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java18
-rw-r--r--services/tests/servicestests/src/com/android/server/vibrator/DeviceVibrationEffectAdapterTest.java24
-rw-r--r--services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/vibrator/RampToStepAdapterTest.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/vibrator/StepToRampAdapterTest.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/vibrator/VibrationScalerTest.java25
-rw-r--r--services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java59
-rw-r--r--services/tests/servicestests/src/com/android/server/vibrator/VibratorControllerTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java33
-rw-r--r--services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java396
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java2
-rwxr-xr-xservices/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java2
-rwxr-xr-xservices/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java3
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java47
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java3
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java5
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java122
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java32
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java21
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java20
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java3
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java3
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java24
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java8
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java8
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java24
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java67
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java3
-rw-r--r--services/usage/java/com/android/server/usage/StorageStatsService.java37
-rw-r--r--services/usage/java/com/android/server/usage/UsageStatsService.java22
-rw-r--r--services/usb/Android.bp1
-rw-r--r--services/usb/java/com/android/server/usb/UsbAlsaDevice.java48
-rw-r--r--services/usb/java/com/android/server/usb/UsbAlsaManager.java8
-rw-r--r--services/usb/java/com/android/server/usb/UsbHostManager.java39
-rw-r--r--services/usb/java/com/android/server/usb/UsbMidiDevice.java3
-rw-r--r--services/usb/java/com/android/server/usb/UsbPortManager.java644
-rw-r--r--services/usb/java/com/android/server/usb/UsbService.java68
-rw-r--r--services/usb/java/com/android/server/usb/UsbUniversalMidiDevice.java469
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbACAudioControlEndpoint.java4
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbACAudioStreamEndpoint.java4
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbACEndpoint.java26
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbACMidi10Endpoint.java (renamed from services/usb/java/com/android/server/usb/descriptors/UsbACMidiEndpoint.java)22
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbACMidi20Endpoint.java67
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java146
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java5
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java15
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbMSMidiHeader.java11
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbMidiBlockParser.java173
-rw-r--r--services/usb/java/com/android/server/usb/hal/port/RawPortInfo.java151
-rw-r--r--services/usb/java/com/android/server/usb/hal/port/UsbPortAidl.java609
-rw-r--r--services/usb/java/com/android/server/usb/hal/port/UsbPortHal.java196
-rw-r--r--services/usb/java/com/android/server/usb/hal/port/UsbPortHalInstance.java45
-rw-r--r--services/usb/java/com/android/server/usb/hal/port/UsbPortHidl.java500
-rw-r--r--services/wallpapereffectsgeneration/OWNERS4
-rw-r--r--telecomm/java/android/telecom/Call.java301
-rw-r--r--telecomm/java/android/telecom/CallAudioState.java4
-rw-r--r--telecomm/java/android/telecom/CallEndpoint.aidl22
-rw-r--r--telecomm/java/android/telecom/CallEndpoint.java150
-rw-r--r--telecomm/java/android/telecom/CallEndpointCallback.java38
-rw-r--r--telecomm/java/android/telecom/CallEndpointSession.java122
-rw-r--r--telecomm/java/android/telecom/Connection.java13
-rw-r--r--telecomm/java/android/telecom/ConnectionRequest.java12
-rw-r--r--telecomm/java/android/telecom/DisconnectCause.java18
-rwxr-xr-xtelecomm/java/android/telecom/InCallAdapter.java28
-rw-r--r--telecomm/java/android/telecom/InCallService.java148
-rw-r--r--telecomm/java/android/telecom/ParcelableCall.java79
-rw-r--r--telecomm/java/android/telecom/ParcelableConference.java10
-rw-r--r--telecomm/java/android/telecom/ParcelableConnection.java8
-rw-r--r--telecomm/java/android/telecom/ParcelableRttCall.java4
-rw-r--r--telecomm/java/android/telecom/Phone.java23
-rw-r--r--telecomm/java/android/telecom/PhoneAccountSuggestion.java2
-rw-r--r--telecomm/java/android/telecom/StatusHints.java4
-rw-r--r--telecomm/java/android/telecom/TelecomManager.java119
-rw-r--r--telecomm/java/com/android/internal/telecom/ICallEndpointCallback.aidl29
-rw-r--r--telecomm/java/com/android/internal/telecom/ICallEndpointSession.aidl (renamed from libs/WindowManager/Shell/src/com/android/wm/shell/back/BackPreviewHandler.java)31
-rw-r--r--telecomm/java/com/android/internal/telecom/IConnectionService.aidl1
-rwxr-xr-xtelecomm/java/com/android/internal/telecom/IInCallAdapter.aidl5
-rw-r--r--telecomm/java/com/android/internal/telecom/IInCallService.aidl14
-rw-r--r--telecomm/java/com/android/internal/telecom/ITelecomService.aidl18
-rw-r--r--telephony/java/android/telephony/AvailableNetworkInfo.java4
-rw-r--r--telephony/java/android/telephony/BarringInfo.java4
-rw-r--r--telephony/java/android/telephony/CallAttributes.java4
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java121
-rw-r--r--telephony/java/android/telephony/CellIdentityLte.java2
-rw-r--r--telephony/java/android/telephony/CellIdentityTdscdma.java2
-rw-r--r--telephony/java/android/telephony/CellIdentityWcdma.java2
-rw-r--r--telephony/java/android/telephony/CellSignalStrengthNr.java2
-rw-r--r--telephony/java/android/telephony/DataSpecificRegistrationInfo.java2
-rw-r--r--telephony/java/android/telephony/ImsManager.java20
-rw-r--r--telephony/java/android/telephony/NetworkRegistrationInfo.java8
-rw-r--r--telephony/java/android/telephony/PcoData.java21
-rw-r--r--telephony/java/android/telephony/PhoneCapability.java2
-rw-r--r--telephony/java/android/telephony/PreciseDataConnectionState.java4
-rw-r--r--telephony/java/android/telephony/ServiceState.java2
-rw-r--r--telephony/java/android/telephony/SignalStrength.java12
-rw-r--r--telephony/java/android/telephony/SubscriptionManager.java13
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java14
-rw-r--r--telephony/java/android/telephony/ThermalMitigationRequest.java2
-rw-r--r--telephony/java/android/telephony/VisualVoicemailSms.java2
-rw-r--r--telephony/java/android/telephony/data/ApnSetting.java2
-rw-r--r--telephony/java/android/telephony/data/DataCallResponse.java16
-rw-r--r--telephony/java/android/telephony/data/DataProfile.java4
-rw-r--r--telephony/java/android/telephony/data/DataServiceCallback.java4
-rw-r--r--telephony/java/android/telephony/data/Qos.java4
-rw-r--r--telephony/java/android/telephony/data/QosBearerFilter.java8
-rw-r--r--telephony/java/android/telephony/data/QosBearerSession.java4
-rw-r--r--telephony/java/android/telephony/gba/GbaAuthRequest.java2
-rw-r--r--telephony/java/android/telephony/ims/DelegateRequest.java2
-rw-r--r--telephony/java/android/telephony/ims/ImsCallProfile.java2
-rwxr-xr-xtelephony/java/android/telephony/ims/ImsCallSession.java407
-rw-r--r--telephony/java/android/telephony/ims/ImsConferenceState.java2
-rw-r--r--telephony/java/android/telephony/ims/ImsExternalCallState.java4
-rw-r--r--telephony/java/android/telephony/ims/ImsRegistrationAttributes.java2
-rw-r--r--telephony/java/android/telephony/ims/ImsSsData.java4
-rw-r--r--telephony/java/android/telephony/ims/ProvisioningManager.java460
-rw-r--r--telephony/java/android/telephony/ims/RcsContactPresenceTuple.java4
-rw-r--r--telephony/java/android/telephony/ims/RcsContactTerminatedReason.java2
-rw-r--r--telephony/java/android/telephony/ims/RcsContactUceCapability.java4
-rw-r--r--telephony/java/android/telephony/ims/RtpHeaderExtensionType.java2
-rw-r--r--telephony/java/android/telephony/ims/SipDelegateConfiguration.java2
-rw-r--r--telephony/java/android/telephony/ims/aidl/IFeatureProvisioningCallback.aidl28
-rw-r--r--telephony/java/android/telephony/ims/feature/MmTelFeature.java13
-rw-r--r--telephony/java/android/telephony/ims/feature/RcsFeature.java58
-rw-r--r--telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java33
-rw-r--r--telephony/java/android/telephony/mbms/DownloadRequest.java4
-rw-r--r--telephony/java/android/telephony/mbms/FileInfo.java2
-rw-r--r--telephony/java/android/telephony/mbms/FileServiceInfo.java2
-rw-r--r--telephony/java/android/telephony/mbms/ServiceInfo.java8
-rw-r--r--telephony/java/android/telephony/mbms/UriPathPair.java4
-rw-r--r--telephony/java/com/android/internal/telephony/ITelephony.aidl38
-rw-r--r--telephony/java/com/android/internal/telephony/NetworkScanResult.java2
-rw-r--r--telephony/java/com/android/internal/telephony/OperatorInfo.java2
-rw-r--r--telephony/java/com/android/internal/telephony/uicc/IccUtils.java8
-rw-r--r--tests/DataIdleTest/src/com/android/tests/dataidle/DataIdleTest.java74
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt9
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/close/OWNERS2
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt26
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt2
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt122
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/OWNERS2
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt20
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OWNERS4
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt5
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt4
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/OWNERS2
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/rotation/OWNERS2
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml11
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml5
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml13
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java4
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/DialogThemedActivity.java57
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeActivityAutoFocus.java6
-rw-r--r--tests/vcn/java/com/android/server/vcn/VcnTest.java130
-rwxr-xr-xtools/fonts/fontchain_linter.py1
-rw-r--r--tools/lint/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt4
-rw-r--r--tools/lint/checks/src/main/java/com/google/android/lint/EnforcePermissionDetector.kt169
-rw-r--r--tools/lint/checks/src/test/java/com/google/android/lint/EnforcePermissionDetectorTest.kt202
-rw-r--r--wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java20
-rw-r--r--wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java20
1458 files changed, 48939 insertions, 11239 deletions
diff --git a/Android.bp b/Android.bp
index cc754f21902a..72bd6427fab2 100644
--- a/Android.bp
+++ b/Android.bp
@@ -136,6 +136,7 @@ filegroup {
":libcamera_client_aidl",
":libcamera_client_framework_aidl",
":libupdate_engine_aidl",
+ ":logd_aidl",
":resourcemanager_aidl",
":storaged_aidl",
":vold_aidl",
@@ -177,6 +178,7 @@ java_library_with_nonpublic_deps {
soong_config_variables: {
include_nonpublic_framework_api: {
static_libs: [
+ "framework-auxiliary.impl",
"framework-supplementalapi.impl",
],
},
@@ -538,7 +540,9 @@ filegroup {
visibility: ["//visibility:private"],
}
-// These defaults are used for both the jar stubs and the doc stubs.
+// Defaults for all stubs that include the non-updatable framework. These defaults do not include
+// module symbols, so will not compile correctly on their own. Users must add module APIs to the
+// classpath (or sources) somehow.
stubs_defaults {
name: "android-non-updatable-stubs-defaults",
srcs: [":android-non-updatable-stub-sources"],
@@ -546,17 +550,14 @@ stubs_defaults {
system_modules: "none",
java_version: "1.8",
arg_files: ["core/res/AndroidManifest.xml"],
- // TODO(b/147699819): remove below aidl includes.
aidl: {
local_include_dirs: [
- "apex/media/aidl/stable",
"media/aidl",
"telephony/java",
],
include_dirs: [
"frameworks/av/aidl",
"frameworks/native/libs/permission/aidl",
- "packages/modules/Connectivity/framework/aidl-export",
],
},
// These are libs from framework-internal-utils that are required (i.e. being referenced)
@@ -576,6 +577,30 @@ stubs_defaults {
"android.hardware.usb.gadget-V1.0-java",
"android.hardware.vibrator-V1.3-java",
"framework-protos",
+ ],
+ filter_packages: packages_to_document,
+ high_mem: true, // Lots of sources => high memory use, see b/170701554
+ installable: false,
+ annotations_enabled: true,
+ previous_api: ":android.api.public.latest",
+ merge_annotations_dirs: ["metalava-manual"],
+ defaults_visibility: ["//visibility:private"],
+ visibility: ["//frameworks/base/api"],
+}
+
+// Defaults with module APIs in the classpath (mostly from prebuilts).
+// Suitable for compiling android-non-updatable.
+stubs_defaults {
+ name: "module-classpath-stubs-defaults",
+ aidl: {
+ local_include_dirs: [
+ "apex/media/aidl/stable",
+ ],
+ include_dirs: [
+ "packages/modules/Connectivity/framework/aidl-export",
+ ],
+ },
+ libs: [
"art.module.public.api",
"sdk_module-lib_current_framework-tethering",
// There are a few classes from modules used by the core that
@@ -586,14 +611,7 @@ stubs_defaults {
// NOTE: The below can be removed once the prebuilt stub contains IKE.
"sdk_system_current_android.net.ipsec.ike",
],
- filter_packages: packages_to_document,
- high_mem: true, // Lots of sources => high memory use, see b/170701554
- installable: false,
- annotations_enabled: true,
- previous_api: ":android.api.public.latest",
- merge_annotations_dirs: ["metalava-manual"],
defaults_visibility: ["//visibility:private"],
- visibility: ["//frameworks/base/api"],
}
build = [
diff --git a/ApiDocs.bp b/ApiDocs.bp
index 4aecc8fe4504..ca211c188dc8 100644
--- a/ApiDocs.bp
+++ b/ApiDocs.bp
@@ -57,7 +57,10 @@ framework_docs_only_libs = [
stubs_defaults {
name: "android-non-updatable-doc-stubs-defaults",
- defaults: ["android-non-updatable-stubs-defaults"],
+ defaults: [
+ "android-non-updatable-stubs-defaults",
+ "module-classpath-stubs-defaults",
+ ],
srcs: [
// No longer part of the stubs, but are included in the docs.
":android-test-base-sources",
@@ -116,6 +119,7 @@ stubs_defaults {
":i18n.module.public.api{.public.stubs.source}",
":framework-appsearch-sources",
+ ":framework-auxiliary-sources",
":framework-connectivity-sources",
":framework-connectivity-tiramisu-updatable-sources",
":framework-graphics-srcs",
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 77b26f80ab97..7f7380a7b41b 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -32,23 +32,15 @@ soong_config_module_type_import {
}
/////////////////////////////////////////////////////////////////////
-// Common metalava configs
-/////////////////////////////////////////////////////////////////////
-
-stubs_defaults {
- name: "metalava-non-updatable-api-stubs-default",
- defaults: ["android-non-updatable-stubs-defaults"],
- api_levels_annotations_enabled: false,
- defaults_visibility: ["//visibility:private"],
-}
-
-/////////////////////////////////////////////////////////////////////
// These modules provide source files for the stub libraries
/////////////////////////////////////////////////////////////////////
droidstubs {
name: "api-stubs-docs-non-updatable",
- defaults: ["metalava-non-updatable-api-stubs-default"],
+ defaults: [
+ "android-non-updatable-stubs-defaults",
+ "module-classpath-stubs-defaults",
+ ],
args: metalava_framework_docs_args,
check_api: {
current: {
@@ -97,7 +89,10 @@ module_libs = " --show-annotation android.annotation.SystemApi\\(" +
droidstubs {
name: "system-api-stubs-docs-non-updatable",
- defaults: ["metalava-non-updatable-api-stubs-default"],
+ defaults: [
+ "android-non-updatable-stubs-defaults",
+ "module-classpath-stubs-defaults",
+ ],
args: metalava_framework_docs_args + priv_apps,
check_api: {
current: {
@@ -133,7 +128,10 @@ droidstubs {
droidstubs {
name: "test-api-stubs-docs-non-updatable",
- defaults: ["metalava-non-updatable-api-stubs-default"],
+ defaults: [
+ "android-non-updatable-stubs-defaults",
+ "module-classpath-stubs-defaults",
+ ],
args: metalava_framework_docs_args + test + priv_apps_in_stubs,
check_api: {
current: {
@@ -175,7 +173,10 @@ droidstubs {
droidstubs {
name: "module-lib-api-stubs-docs-non-updatable",
- defaults: ["metalava-non-updatable-api-stubs-default"],
+ defaults: [
+ "android-non-updatable-stubs-defaults",
+ "module-classpath-stubs-defaults",
+ ],
args: metalava_framework_docs_args + priv_apps_in_stubs + module_libs,
check_api: {
current: {
@@ -285,6 +286,7 @@ java_library_with_nonpublic_deps {
soong_config_variables: {
include_nonpublic_framework_api: {
libs: [
+ "framework-auxiliary.stubs",
"framework-supplementalapi.stubs",
],
},
@@ -302,6 +304,7 @@ java_library_with_nonpublic_deps {
soong_config_variables: {
include_nonpublic_framework_api: {
libs: [
+ "framework-auxiliary.stubs",
"framework-supplementalapi.stubs",
],
},
@@ -334,6 +337,7 @@ java_library_with_nonpublic_deps {
soong_config_variables: {
include_nonpublic_framework_api: {
libs: [
+ "framework-auxiliary.stubs",
"framework-supplementalapi.stubs",
],
},
@@ -362,6 +366,7 @@ java_library_with_nonpublic_deps {
soong_config_variables: {
include_nonpublic_framework_api: {
static_libs: [
+ "framework-auxiliary.stubs",
"framework-supplementalapi.stubs",
],
},
@@ -378,6 +383,7 @@ java_library_with_nonpublic_deps {
soong_config_variables: {
include_nonpublic_framework_api: {
static_libs: [
+ "framework-auxiliary.stubs",
"framework-supplementalapi.stubs",
],
},
@@ -410,6 +416,7 @@ java_library_with_nonpublic_deps {
soong_config_variables: {
include_nonpublic_framework_api: {
static_libs: [
+ "framework-auxiliary.stubs",
"framework-supplementalapi.stubs",
],
},
diff --git a/apct-tests/perftests/core/src/android/view/HandwritingInitiatorPerfTest.java b/apct-tests/perftests/core/src/android/view/HandwritingInitiatorPerfTest.java
index cf94e9e0d384..4cd974141d26 100644
--- a/apct-tests/perftests/core/src/android/view/HandwritingInitiatorPerfTest.java
+++ b/apct-tests/perftests/core/src/android/view/HandwritingInitiatorPerfTest.java
@@ -25,7 +25,6 @@ import static android.view.MotionEvent.TOOL_TYPE_STYLUS;
import android.app.Instrumentation;
import android.content.Context;
-import android.graphics.Rect;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
import android.view.inputmethod.EditorInfo;
@@ -190,7 +189,7 @@ public class HandwritingInitiatorPerfTest {
final View view = new View(mContext);
final EditorInfo editorInfo = new EditorInfo();
while (state.keepRunning()) {
- mHandwritingInitiator.onInputConnectionCreated(view, editorInfo);
+ mHandwritingInitiator.onInputConnectionCreated(view);
state.pauseTiming();
mHandwritingInitiator.onInputConnectionClosed(view);
state.resumeTiming();
@@ -201,24 +200,14 @@ public class HandwritingInitiatorPerfTest {
public void onInputConnectionClosed() {
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
final View view = new View(mContext);
- final EditorInfo editorInfo = new EditorInfo();
while (state.keepRunning()) {
state.pauseTiming();
- mHandwritingInitiator.onInputConnectionCreated(view, editorInfo);
+ mHandwritingInitiator.onInputConnectionCreated(view);
state.resumeTiming();
mHandwritingInitiator.onInputConnectionClosed(view);
}
}
- @Test
- public void updateEditorBoundary() {
- final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
- final Rect rect = new Rect(0, 0, 100, 100);
- while (state.keepRunning()) {
- mHandwritingInitiator.updateEditorBound(rect);
- }
- }
-
private MotionEvent createMotionEvent(int action, int toolType, int x, int y, long eventTime) {
MotionEvent.PointerProperties[] properties = MotionEvent.PointerProperties.createArray(1);
properties[0].toolType = toolType;
diff --git a/apex/blobstore/framework/java/android/app/blob/BlobInfo.java b/apex/blobstore/framework/java/android/app/blob/BlobInfo.java
index ba92d95b483e..73ef310c7b40 100644
--- a/apex/blobstore/framework/java/android/app/blob/BlobInfo.java
+++ b/apex/blobstore/framework/java/android/app/blob/BlobInfo.java
@@ -48,6 +48,7 @@ public final class BlobInfo implements Parcelable {
mLeaseInfos = leaseInfos;
}
+ @SuppressWarnings("UnsafeParcelApi")
private BlobInfo(Parcel in) {
mId = in.readLong();
mExpiryTimeMs = in.readLong();
diff --git a/apex/jobscheduler/framework/java/android/app/AlarmManager.java b/apex/jobscheduler/framework/java/android/app/AlarmManager.java
index 9c0c3657bff3..66767e21a2e7 100644
--- a/apex/jobscheduler/framework/java/android/app/AlarmManager.java
+++ b/apex/jobscheduler/framework/java/android/app/AlarmManager.java
@@ -1408,6 +1408,7 @@ public class AlarmManager {
* Use the {@link #CREATOR}
* @hide
*/
+ @SuppressWarnings("UnsafeParcelApi")
AlarmClockInfo(Parcel in) {
mTriggerTime = in.readLong();
mShowIntent = in.readParcelable(PendingIntent.class.getClassLoader());
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
index 0e6006a62397..b9673f25d680 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
@@ -881,6 +881,7 @@ public class JobInfo implements Parcelable {
return hashCode;
}
+ @SuppressWarnings("UnsafeParcelApi")
private JobInfo(Parcel in) {
jobId = in.readInt();
extras = in.readPersistableBundle();
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 12a8654c61f7..d93ad3c0d8bc 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -1633,12 +1633,9 @@ public class JobSchedulerService extends com.android.server.SystemService
for (int i=0; i<mActiveServices.size(); i++) {
final JobServiceContext jsc = mActiveServices.get(i);
final JobStatus job = jsc.getRunningJobLocked();
- if (job != null
- && !job.canRunInDoze()
- && !job.dozeWhitelisted
- && !job.uidActive) {
- // We will report active if we have a job running and it is not an exception
- // due to being in the foreground or whitelisted.
+ if (job != null && !job.canRunInDoze()) {
+ // We will report active if we have a job running and it does not have an
+ // exception that allows it to run in Doze.
active = true;
break;
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java
index 090260cb8eb3..f6de109d7ec9 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java
@@ -246,7 +246,7 @@ public final class DeviceIdleJobsController extends StateController {
pw.print((jobStatus.satisfiedConstraints
& JobStatus.CONSTRAINT_DEVICE_NOT_DOZING) != 0
? " RUNNABLE" : " WAITING");
- if (jobStatus.dozeWhitelisted) {
+ if (jobStatus.appHasDozeExemption) {
pw.print(" WHITELISTED");
}
if (mAllowInIdleJobs.contains(jobStatus)) {
@@ -273,7 +273,7 @@ public final class DeviceIdleJobsController extends StateController {
proto.write(TrackedJob.SOURCE_PACKAGE_NAME, jobStatus.getSourcePackageName());
proto.write(TrackedJob.ARE_CONSTRAINTS_SATISFIED,
(jobStatus.satisfiedConstraints & JobStatus.CONSTRAINT_DEVICE_NOT_DOZING) != 0);
- proto.write(TrackedJob.IS_DOZE_WHITELISTED, jobStatus.dozeWhitelisted);
+ proto.write(TrackedJob.IS_DOZE_WHITELISTED, jobStatus.appHasDozeExemption);
proto.write(TrackedJob.IS_ALLOWED_IN_DOZE, mAllowInIdleJobs.contains(jobStatus));
proto.end(jsToken);
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 f74a4facd24c..0eea70106608 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
@@ -267,7 +267,7 @@ public final class JobStatus {
private final boolean mHasMediaBackupExemption;
// Set to true if doze constraint was satisfied due to app being whitelisted.
- public boolean dozeWhitelisted;
+ boolean appHasDozeExemption;
// Set to true when the app is "active" per AppStateTracker
public boolean uidActive;
@@ -1179,7 +1179,8 @@ public final class JobStatus {
* in Doze.
*/
public boolean canRunInDoze() {
- return (getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0
+ return appHasDozeExemption
+ || (getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0
|| ((shouldTreatAsExpeditedJob() || startedAsExpeditedJob)
&& (mDynamicConstraints & CONSTRAINT_DEVICE_NOT_DOZING) == 0);
}
@@ -1243,7 +1244,7 @@ public final class JobStatus {
/** @return true if the constraint was changed, false otherwise. */
boolean setDeviceNotDozingConstraintSatisfied(final long nowElapsed,
boolean state, boolean whitelisted) {
- dozeWhitelisted = whitelisted;
+ appHasDozeExemption = whitelisted;
if (setConstraintSatisfied(CONSTRAINT_DEVICE_NOT_DOZING, nowElapsed, state)) {
// The constraint was changed. Update the ready flag.
mReadyNotDozing = state || canRunInDoze();
@@ -2110,7 +2111,7 @@ public final class JobStatus {
}
pw.decreaseIndent();
- if (dozeWhitelisted) {
+ if (appHasDozeExemption) {
pw.println("Doze whitelisted: true");
}
if (uidActive) {
@@ -2323,7 +2324,7 @@ public final class JobStatus {
dumpConstraints(proto, JobStatusDumpProto.SATISFIED_CONSTRAINTS, satisfiedConstraints);
dumpConstraints(proto, JobStatusDumpProto.UNSATISFIED_CONSTRAINTS,
((requiredConstraints | CONSTRAINT_WITHIN_QUOTA) & ~satisfiedConstraints));
- proto.write(JobStatusDumpProto.IS_DOZE_WHITELISTED, dozeWhitelisted);
+ proto.write(JobStatusDumpProto.IS_DOZE_WHITELISTED, appHasDozeExemption);
proto.write(JobStatusDumpProto.IS_UID_ACTIVE, uidActive);
proto.write(JobStatusDumpProto.IS_EXEMPTED_FROM_APP_STANDBY,
job.isExemptedFromAppStandby());
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
index 65e1d49d1510..dd5246aebbb4 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
@@ -36,6 +36,7 @@ import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.AlarmManager;
import android.app.IUidObserver;
+import android.app.job.JobInfo;
import android.app.usage.UsageEvents;
import android.app.usage.UsageStatsManagerInternal;
import android.app.usage.UsageStatsManagerInternal.UsageEventListener;
@@ -161,6 +162,28 @@ public final class QuotaController extends StateController {
public long inQuotaTimeElapsed;
/**
+ * The time after which the app will be under the bucket quota and can start running
+ * low priority jobs again. This is only valid if
+ * {@link #executionTimeInWindowMs} >=
+ * {@link #mAllowedTimePerPeriodMs} * (1 - {@link #mAllowedTimeSurplusPriorityLow}),
+ * {@link #executionTimeInMaxPeriodMs} >= {@link #mMaxExecutionTimeMs},
+ * {@link #bgJobCountInWindow} >= {@link #jobCountLimit}, or
+ * {@link #sessionCountInWindow} >= {@link #sessionCountLimit}.
+ */
+ public long inQuotaTimeLowElapsed;
+
+ /**
+ * The time after which the app will be under the bucket quota and can start running
+ * min priority jobs again. This is only valid if
+ * {@link #executionTimeInWindowMs} >=
+ * {@link #mAllowedTimePerPeriodMs} * (1 - {@link #mAllowedTimeSurplusPriorityMin}),
+ * {@link #executionTimeInMaxPeriodMs} >= {@link #mMaxExecutionTimeMs},
+ * {@link #bgJobCountInWindow} >= {@link #jobCountLimit}, or
+ * {@link #sessionCountInWindow} >= {@link #sessionCountLimit}.
+ */
+ public long inQuotaTimeMinElapsed;
+
+ /**
* The time after which {@link #jobCountInRateLimitingWindow} should be considered invalid,
* in the elapsed realtime timebase.
*/
@@ -199,6 +222,8 @@ public final class QuotaController extends StateController {
+ "bgJobCountInMaxPeriod=" + bgJobCountInMaxPeriod + ", "
+ "sessionCountInWindow=" + sessionCountInWindow + ", "
+ "inQuotaTime=" + inQuotaTimeElapsed + ", "
+ + "inQuotaTimeLow=" + inQuotaTimeLowElapsed + ", "
+ + "inQuotaTimeMin=" + inQuotaTimeMinElapsed + ", "
+ "rateLimitJobCountExpirationTime=" + jobRateLimitExpirationTimeElapsed + ", "
+ "rateLimitJobCountWindow=" + jobCountInRateLimitingWindow + ", "
+ "rateLimitSessionCountExpirationTime="
@@ -351,6 +376,24 @@ public final class QuotaController extends StateController {
*/
private long mMaxExecutionTimeIntoQuotaMs = mMaxExecutionTimeMs - mQuotaBufferMs;
+ /**
+ * The percentage of {@link #mAllowedTimePerPeriodMs} that should not be used by
+ * {@link JobInfo#PRIORITY_LOW low priority} jobs. In other words, there must be a minimum
+ * surplus of this amount of remaining allowed time before we start running low priority
+ * jobs.
+ */
+ private float mAllowedTimeSurplusPriorityLow =
+ QcConstants.DEFAULT_ALLOWED_TIME_SURPLUS_PRIORITY_LOW;
+
+ /**
+ * The percentage of {@link #mAllowedTimePerPeriodMs} that should not be used by
+ * {@link JobInfo#PRIORITY_MIN min priority} jobs. In other words, there must be a minimum
+ * surplus of this amount of remaining allowed time before we start running low priority
+ * jobs.
+ */
+ private float mAllowedTimeSurplusPriorityMin =
+ QcConstants.DEFAULT_ALLOWED_TIME_SURPLUS_PRIORITY_MIN;
+
/** The period of time used to rate limit recently run jobs. */
private long mRateLimitingWindowMs = QcConstants.DEFAULT_RATE_LIMITING_WINDOW_MS;
@@ -653,10 +696,11 @@ public final class QuotaController extends StateController {
boolean forUpdate) {
if (jobStatus.clearTrackingController(JobStatus.TRACKING_QUOTA)) {
unprepareFromExecutionLocked(jobStatus);
- ArraySet<JobStatus> jobs = mTrackedJobs.get(jobStatus.getSourceUserId(),
- jobStatus.getSourcePackageName());
- if (jobs != null) {
- jobs.remove(jobStatus);
+ final int userId = jobStatus.getSourceUserId();
+ final String pkgName = jobStatus.getSourcePackageName();
+ ArraySet<JobStatus> jobs = mTrackedJobs.get(userId, pkgName);
+ if (jobs != null && jobs.remove(jobStatus) && jobs.size() == 0) {
+ mInQuotaAlarmQueue.removeAlarmForKey(new Package(userId, pkgName));
}
}
}
@@ -771,7 +815,8 @@ public final class QuotaController extends StateController {
return mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS;
}
return getTimeUntilQuotaConsumedLocked(
- jobStatus.getSourceUserId(), jobStatus.getSourcePackageName());
+ jobStatus.getSourceUserId(), jobStatus.getSourcePackageName(),
+ jobStatus.getEffectivePriority());
}
// Expedited job.
@@ -856,7 +901,8 @@ public final class QuotaController extends StateController {
return isTopStartedJobLocked(jobStatus)
|| isUidInForeground(jobStatus.getSourceUid())
|| isWithinQuotaLocked(
- jobStatus.getSourceUserId(), jobStatus.getSourcePackageName(), standbyBucket);
+ jobStatus.getSourceUserId(), jobStatus.getSourcePackageName(), standbyBucket,
+ jobStatus.getEffectivePriority());
}
@GuardedBy("mLock")
@@ -873,7 +919,7 @@ public final class QuotaController extends StateController {
@VisibleForTesting
@GuardedBy("mLock")
boolean isWithinQuotaLocked(final int userId, @NonNull final String packageName,
- final int standbyBucket) {
+ final int standbyBucket, final int priority) {
if (!mIsEnabled) {
return true;
}
@@ -881,9 +927,16 @@ public final class QuotaController extends StateController {
if (isQuotaFreeLocked(standbyBucket)) return true;
+ final long minSurplus;
+ if (priority <= JobInfo.PRIORITY_MIN) {
+ minSurplus = (long) (mAllowedTimePerPeriodMs * mAllowedTimeSurplusPriorityMin);
+ } else if (priority <= JobInfo.PRIORITY_LOW) {
+ minSurplus = (long) (mAllowedTimePerPeriodMs * mAllowedTimeSurplusPriorityLow);
+ } else {
+ minSurplus = 0;
+ }
ExecutionStats stats = getExecutionStatsLocked(userId, packageName, standbyBucket);
- // TODO: use a higher minimum remaining time for jobs with MINIMUM priority
- return getRemainingExecutionTimeLocked(stats) > 0
+ return getRemainingExecutionTimeLocked(stats) > minSurplus
&& isUnderJobCountQuotaLocked(stats, standbyBucket)
&& isUnderSessionCountQuotaLocked(stats, standbyBucket);
}
@@ -1001,7 +1054,8 @@ public final class QuotaController extends StateController {
* job is running.
*/
@VisibleForTesting
- long getTimeUntilQuotaConsumedLocked(final int userId, @NonNull final String packageName) {
+ long getTimeUntilQuotaConsumedLocked(final int userId, @NonNull final String packageName,
+ @JobInfo.Priority int jobPriority) {
final long nowElapsed = sElapsedRealtimeClock.millis();
final int standbyBucket = JobSchedulerService.standbyBucketForPackage(
packageName, userId, nowElapsed);
@@ -1022,10 +1076,15 @@ public final class QuotaController extends StateController {
final long startWindowElapsed = nowElapsed - stats.windowSizeMs;
final long startMaxElapsed = nowElapsed - MAX_PERIOD_MS;
- final long allowedTimeRemainingMs = mAllowedTimePerPeriodMs - stats.executionTimeInWindowMs;
+ final long allowedTimePerPeriodMs = getAllowedTimePerPeriodMs(jobPriority);
+ final long allowedTimeRemainingMs = allowedTimePerPeriodMs - stats.executionTimeInWindowMs;
final long maxExecutionTimeRemainingMs =
mMaxExecutionTimeMs - stats.executionTimeInMaxPeriodMs;
+ if (allowedTimeRemainingMs <= 0 || maxExecutionTimeRemainingMs <= 0) {
+ return 0;
+ }
+
// Regular ACTIVE case. Since the bucket size equals the allowed time, the app jobs can
// essentially run until they reach the maximum limit.
if (stats.windowSizeMs == mAllowedTimePerPeriodMs) {
@@ -1044,6 +1103,16 @@ public final class QuotaController extends StateController {
sessions, startWindowElapsed, allowedTimeRemainingMs));
}
+ private long getAllowedTimePerPeriodMs(@JobInfo.Priority int jobPriority) {
+ if (jobPriority <= JobInfo.PRIORITY_MIN) {
+ return (long) (mAllowedTimePerPeriodMs * (1 - mAllowedTimeSurplusPriorityMin));
+ }
+ if (jobPriority <= JobInfo.PRIORITY_LOW) {
+ return (long) (mAllowedTimePerPeriodMs * (1 - mAllowedTimeSurplusPriorityLow));
+ }
+ return mAllowedTimePerPeriodMs;
+ }
+
/**
* Calculates how much time it will take, in milliseconds, until the quota is fully consumed.
*
@@ -1198,10 +1267,13 @@ public final class QuotaController extends StateController {
stats.sessionCountInWindow = 0;
if (stats.jobCountLimit == 0 || stats.sessionCountLimit == 0) {
// App won't be in quota until configuration changes.
- stats.inQuotaTimeElapsed = Long.MAX_VALUE;
+ stats.inQuotaTimeElapsed = stats.inQuotaTimeLowElapsed = stats.inQuotaTimeMinElapsed =
+ Long.MAX_VALUE;
} else {
stats.inQuotaTimeElapsed = 0;
}
+ final long allowedTimeMinMs = getAllowedTimePerPeriodMs(JobInfo.PRIORITY_MIN);
+ final long allowedTimeLowMs = getAllowedTimePerPeriodMs(JobInfo.PRIORITY_LOW);
Timer timer = mPkgTimers.get(userId, packageName);
final long nowElapsed = sElapsedRealtimeClock.millis();
@@ -1219,13 +1291,25 @@ public final class QuotaController extends StateController {
stats.inQuotaTimeElapsed = Math.max(stats.inQuotaTimeElapsed,
nowElapsed - mAllowedTimeIntoQuotaMs + stats.windowSizeMs);
}
+ if (stats.executionTimeInWindowMs >= allowedTimeLowMs) {
+ stats.inQuotaTimeLowElapsed = Math.max(stats.inQuotaTimeLowElapsed,
+ nowElapsed - allowedTimeLowMs + stats.windowSizeMs);
+ }
+ if (stats.executionTimeInWindowMs >= allowedTimeMinMs) {
+ stats.inQuotaTimeMinElapsed = Math.max(stats.inQuotaTimeMinElapsed,
+ nowElapsed - allowedTimeMinMs + stats.windowSizeMs);
+ }
if (stats.executionTimeInMaxPeriodMs >= mMaxExecutionTimeIntoQuotaMs) {
- stats.inQuotaTimeElapsed = Math.max(stats.inQuotaTimeElapsed,
- nowElapsed - mMaxExecutionTimeIntoQuotaMs + MAX_PERIOD_MS);
+ final long inQuotaTime = nowElapsed - mMaxExecutionTimeIntoQuotaMs + MAX_PERIOD_MS;
+ stats.inQuotaTimeElapsed = Math.max(stats.inQuotaTimeElapsed, inQuotaTime);
+ stats.inQuotaTimeLowElapsed = Math.max(stats.inQuotaTimeLowElapsed, inQuotaTime);
+ stats.inQuotaTimeMinElapsed = Math.max(stats.inQuotaTimeMinElapsed, inQuotaTime);
}
if (stats.bgJobCountInWindow >= stats.jobCountLimit) {
- stats.inQuotaTimeElapsed = Math.max(stats.inQuotaTimeElapsed,
- nowElapsed + stats.windowSizeMs);
+ final long inQuotaTime = nowElapsed + stats.windowSizeMs;
+ stats.inQuotaTimeElapsed = Math.max(stats.inQuotaTimeElapsed, inQuotaTime);
+ stats.inQuotaTimeLowElapsed = Math.max(stats.inQuotaTimeLowElapsed, inQuotaTime);
+ stats.inQuotaTimeMinElapsed = Math.max(stats.inQuotaTimeMinElapsed, inQuotaTime);
}
}
@@ -1267,9 +1351,23 @@ public final class QuotaController extends StateController {
start + stats.executionTimeInWindowMs - mAllowedTimeIntoQuotaMs
+ stats.windowSizeMs);
}
+ if (stats.executionTimeInWindowMs >= allowedTimeLowMs) {
+ stats.inQuotaTimeLowElapsed = Math.max(stats.inQuotaTimeLowElapsed,
+ start + stats.executionTimeInWindowMs - allowedTimeLowMs
+ + stats.windowSizeMs);
+ }
+ if (stats.executionTimeInWindowMs >= allowedTimeMinMs) {
+ stats.inQuotaTimeMinElapsed = Math.max(stats.inQuotaTimeMinElapsed,
+ start + stats.executionTimeInWindowMs - allowedTimeMinMs
+ + stats.windowSizeMs);
+ }
if (stats.bgJobCountInWindow >= stats.jobCountLimit) {
- stats.inQuotaTimeElapsed = Math.max(stats.inQuotaTimeElapsed,
- session.endTimeElapsed + stats.windowSizeMs);
+ final long inQuotaTime = session.endTimeElapsed + stats.windowSizeMs;
+ stats.inQuotaTimeElapsed = Math.max(stats.inQuotaTimeElapsed, inQuotaTime);
+ stats.inQuotaTimeLowElapsed = Math.max(stats.inQuotaTimeLowElapsed,
+ inQuotaTime);
+ stats.inQuotaTimeMinElapsed = Math.max(stats.inQuotaTimeMinElapsed,
+ inQuotaTime);
}
if (i == loopStart
|| (sessions.get(i + 1).startTimeElapsed - session.endTimeElapsed)
@@ -1278,8 +1376,12 @@ public final class QuotaController extends StateController {
sessionCountInWindow++;
if (sessionCountInWindow >= stats.sessionCountLimit) {
- stats.inQuotaTimeElapsed = Math.max(stats.inQuotaTimeElapsed,
- session.endTimeElapsed + stats.windowSizeMs);
+ final long inQuotaTime = session.endTimeElapsed + stats.windowSizeMs;
+ stats.inQuotaTimeElapsed = Math.max(stats.inQuotaTimeElapsed, inQuotaTime);
+ stats.inQuotaTimeLowElapsed = Math.max(stats.inQuotaTimeLowElapsed,
+ inQuotaTime);
+ stats.inQuotaTimeMinElapsed = Math.max(stats.inQuotaTimeMinElapsed,
+ inQuotaTime);
}
}
}
@@ -1425,10 +1527,9 @@ public final class QuotaController extends StateController {
synchronized (mLock) {
final long nowElapsed = sElapsedRealtimeClock.millis();
final ShrinkableDebits quota = getEJDebitsLocked(userId, packageName);
- if (transactQuotaLocked(userId, packageName, nowElapsed, quota, credit)
- && maybeUpdateConstraintForPkgLocked(nowElapsed, userId, packageName)) {
- mStateChangedListener
- .onControllerStateChanged(mTrackedJobs.get(userId, packageName));
+ if (transactQuotaLocked(userId, packageName, nowElapsed, quota, credit)) {
+ mStateChangedListener.onControllerStateChanged(
+ maybeUpdateConstraintForPkgLocked(nowElapsed, userId, packageName));
}
}
}
@@ -1558,9 +1659,8 @@ public final class QuotaController extends StateController {
final int userId = mTrackedJobs.keyAt(u);
for (int p = 0; p < mTrackedJobs.numElementsForKey(userId); ++p) {
final String packageName = mTrackedJobs.keyAt(u, p);
- if (maybeUpdateConstraintForPkgLocked(nowElapsed, userId, packageName)) {
- changedJobs.addAll(mTrackedJobs.valueAt(u, p));
- }
+ changedJobs.addAll(
+ maybeUpdateConstraintForPkgLocked(nowElapsed, userId, packageName));
}
}
if (changedJobs.size() > 0) {
@@ -1573,18 +1673,20 @@ public final class QuotaController extends StateController {
*
* @return true if at least one job had its bit changed
*/
- private boolean maybeUpdateConstraintForPkgLocked(final long nowElapsed, final int userId,
- @NonNull final String packageName) {
+ @NonNull
+ private ArraySet<JobStatus> maybeUpdateConstraintForPkgLocked(final long nowElapsed,
+ final int userId, @NonNull final String packageName) {
ArraySet<JobStatus> jobs = mTrackedJobs.get(userId, packageName);
+ final ArraySet<JobStatus> changedJobs = new ArraySet<>();
if (jobs == null || jobs.size() == 0) {
- return false;
+ return changedJobs;
}
// Quota is the same for all jobs within a package.
final int realStandbyBucket = jobs.valueAt(0).getStandbyBucket();
- final boolean realInQuota = isWithinQuotaLocked(userId, packageName, realStandbyBucket);
+ final boolean realInQuota = isWithinQuotaLocked(
+ userId, packageName, realStandbyBucket, JobInfo.PRIORITY_DEFAULT);
boolean outOfEJQuota = false;
- boolean changed = false;
for (int i = jobs.size() - 1; i >= 0; --i) {
final JobStatus js = jobs.valueAt(i);
final boolean isWithinEJQuota =
@@ -1592,21 +1694,30 @@ public final class QuotaController extends StateController {
if (isTopStartedJobLocked(js)) {
// Job was started while the app was in the TOP state so we should allow it to
// finish.
- changed |= js.setQuotaConstraintSatisfied(nowElapsed, true);
+ if (js.setQuotaConstraintSatisfied(nowElapsed, true)) {
+ changedJobs.add(js);
+ }
} else if (realStandbyBucket != ACTIVE_INDEX
- && realStandbyBucket == js.getEffectiveStandbyBucket()) {
+ && realStandbyBucket == js.getEffectiveStandbyBucket()
+ && js.getEffectivePriority() >= JobInfo.PRIORITY_DEFAULT) {
// An app in the ACTIVE bucket may be out of quota while the job could be in quota
// for some reason. Therefore, avoid setting the real value here and check each job
// individually.
- changed |= setConstraintSatisfied(js, nowElapsed, isWithinEJQuota || realInQuota);
+ if (setConstraintSatisfied(js, nowElapsed, isWithinEJQuota || realInQuota)) {
+ changedJobs.add(js);
+ }
} else {
// This job is somehow exempted. Need to determine its own quota status.
- changed |= setConstraintSatisfied(js, nowElapsed,
- isWithinEJQuota || isWithinQuotaLocked(js));
+ if (setConstraintSatisfied(js, nowElapsed,
+ isWithinEJQuota || isWithinQuotaLocked(js))) {
+ changedJobs.add(js);
+ }
}
if (js.isRequestedExpeditedJob()) {
- changed |= setExpeditedQuotaApproved(js, nowElapsed, isWithinEJQuota);
+ if (setExpeditedQuotaApproved(js, nowElapsed, isWithinEJQuota)) {
+ changedJobs.add(js);
+ }
outOfEJQuota |= !isWithinEJQuota;
}
}
@@ -1618,7 +1729,7 @@ public final class QuotaController extends StateController {
} else {
mInQuotaAlarmQueue.removeAlarmForKey(new Package(userId, packageName));
}
- return changed;
+ return changedJobs;
}
private class UidConstraintUpdater implements Consumer<JobStatus> {
@@ -1651,9 +1762,9 @@ public final class QuotaController extends StateController {
final int userId = jobStatus.getSourceUserId();
final String packageName = jobStatus.getSourcePackageName();
final int realStandbyBucket = jobStatus.getStandbyBucket();
- if (isWithinQuotaLocked(userId, packageName, realStandbyBucket) && isWithinEJQuota) {
- // TODO(141645789): we probably shouldn't cancel the alarm until we've verified
- // that all jobs for the userId-package are within quota.
+ if (isWithinEJQuota
+ && isWithinQuotaLocked(userId, packageName, realStandbyBucket,
+ JobInfo.PRIORITY_MIN)) {
mInQuotaAlarmQueue.removeAlarmForKey(new Package(userId, packageName));
} else {
mToScheduleStartAlarms.add(userId, packageName, realStandbyBucket);
@@ -1700,16 +1811,41 @@ public final class QuotaController extends StateController {
return;
}
+ ArraySet<JobStatus> jobs = mTrackedJobs.get(userId, packageName);
+ if (jobs == null || jobs.size() == 0) {
+ Slog.e(TAG, "maybeScheduleStartAlarmLocked called for "
+ + packageToString(userId, packageName) + " that has no jobs");
+ mInQuotaAlarmQueue.removeAlarmForKey(new Package(userId, packageName));
+ return;
+ }
+
ExecutionStats stats = getExecutionStatsLocked(userId, packageName, standbyBucket);
final boolean isUnderJobCountQuota = isUnderJobCountQuotaLocked(stats, standbyBucket);
final boolean isUnderTimingSessionCountQuota = isUnderSessionCountQuotaLocked(stats,
standbyBucket);
final long remainingEJQuota = getRemainingEJExecutionTimeLocked(userId, packageName);
- final boolean inRegularQuota = stats.executionTimeInWindowMs < mAllowedTimePerPeriodMs
- && stats.executionTimeInMaxPeriodMs < mMaxExecutionTimeMs
- && isUnderJobCountQuota
- && isUnderTimingSessionCountQuota;
+ int minPriority = JobInfo.PRIORITY_MAX;
+ boolean hasDefPlus = false, hasLow = false, hasMin = false;
+ for (int i = jobs.size() - 1; i >= 0; --i) {
+ final int priority = jobs.valueAt(i).getEffectivePriority();
+ minPriority = Math.min(minPriority, priority);
+ if (priority <= JobInfo.PRIORITY_MIN) {
+ hasMin = true;
+ } else if (priority <= JobInfo.PRIORITY_LOW) {
+ hasLow = true;
+ } else {
+ hasDefPlus = true;
+ }
+ if (hasMin && hasLow && hasDefPlus) {
+ break;
+ }
+ }
+ final boolean inRegularQuota =
+ stats.executionTimeInWindowMs < getAllowedTimePerPeriodMs(minPriority)
+ && stats.executionTimeInMaxPeriodMs < mMaxExecutionTimeMs
+ && isUnderJobCountQuota
+ && isUnderTimingSessionCountQuota;
if (inRegularQuota && remainingEJQuota > 0) {
// Already in quota. Why was this method called?
if (DEBUG) {
@@ -1728,7 +1864,24 @@ public final class QuotaController extends StateController {
long inEJQuotaTimeElapsed = Long.MAX_VALUE;
if (!inRegularQuota) {
// The time this app will have quota again.
- long inQuotaTimeElapsed = stats.inQuotaTimeElapsed;
+ long executionInQuotaTime = Long.MAX_VALUE;
+ boolean hasExecutionInQuotaTime = false;
+ if (hasMin && stats.inQuotaTimeMinElapsed > 0) {
+ executionInQuotaTime = Math.min(executionInQuotaTime, stats.inQuotaTimeMinElapsed);
+ hasExecutionInQuotaTime = true;
+ }
+ if (hasLow && stats.inQuotaTimeLowElapsed > 0) {
+ executionInQuotaTime = Math.min(executionInQuotaTime, stats.inQuotaTimeLowElapsed);
+ hasExecutionInQuotaTime = true;
+ }
+ if (hasDefPlus && stats.inQuotaTimeElapsed > 0) {
+ executionInQuotaTime = Math.min(executionInQuotaTime, stats.inQuotaTimeElapsed);
+ hasExecutionInQuotaTime = true;
+ }
+ long inQuotaTimeElapsed = 0;
+ if (hasExecutionInQuotaTime) {
+ inQuotaTimeElapsed = executionInQuotaTime;
+ }
if (!isUnderJobCountQuota && stats.bgJobCountInWindow < stats.jobCountLimit) {
// App hit the rate limit.
inQuotaTimeElapsed =
@@ -1941,6 +2094,7 @@ public final class QuotaController extends StateController {
private final ArraySet<JobStatus> mRunningBgJobs = new ArraySet<>();
private long mStartTimeElapsed;
private int mBgJobCount;
+ private int mLowestPriority = JobInfo.PRIORITY_MAX;
private long mDebitAdjustment;
Timer(int uid, int userId, String packageName, boolean regularJobTimer) {
@@ -1963,6 +2117,7 @@ public final class QuotaController extends StateController {
Slog.v(TAG, "Starting to track " + jobStatus.toShortString());
}
// Always maintain list of running jobs, even when quota is free.
+ mLowestPriority = Math.min(mLowestPriority, jobStatus.getEffectivePriority());
if (mRunningBgJobs.add(jobStatus) && shouldTrackLocked()) {
mBgJobCount++;
if (mRegularJobTimer) {
@@ -2002,6 +2157,13 @@ public final class QuotaController extends StateController {
&& !isQuotaFreeLocked(standbyBucket)) {
emitSessionLocked(nowElapsed);
cancelCutoff();
+ mLowestPriority = JobInfo.PRIORITY_MAX;
+ } else if (mLowestPriority == jobStatus.getEffectivePriority()) {
+ mLowestPriority = JobInfo.PRIORITY_MAX;
+ for (int i = mRunningBgJobs.size() - 1; i >= 0; --i) {
+ mLowestPriority = Math.min(mLowestPriority,
+ mRunningBgJobs.valueAt(i).getEffectivePriority());
+ }
}
}
}
@@ -2128,9 +2290,14 @@ public final class QuotaController extends StateController {
}
Message msg = mHandler.obtainMessage(
mRegularJobTimer ? MSG_REACHED_QUOTA : MSG_REACHED_EJ_QUOTA, mPkg);
- final long timeRemainingMs = mRegularJobTimer
- ? getTimeUntilQuotaConsumedLocked(mPkg.userId, mPkg.packageName)
- : getTimeUntilEJQuotaConsumedLocked(mPkg.userId, mPkg.packageName);
+ final long timeRemainingMs;
+ if (mRegularJobTimer) {
+ timeRemainingMs = getTimeUntilQuotaConsumedLocked(
+ mPkg.userId, mPkg.packageName, mLowestPriority);
+ } else {
+ timeRemainingMs =
+ getTimeUntilEJQuotaConsumedLocked(mPkg.userId, mPkg.packageName);
+ }
if (DEBUG) {
Slog.i(TAG,
(mRegularJobTimer ? "Regular job" : "EJ") + " for " + mPkg + " has "
@@ -2250,11 +2417,10 @@ public final class QuotaController extends StateController {
final ShrinkableDebits debits =
getEJDebitsLocked(mPkg.userId, mPkg.packageName);
if (transactQuotaLocked(mPkg.userId, mPkg.packageName,
- nowElapsed, debits, pendingReward)
- && maybeUpdateConstraintForPkgLocked(nowElapsed,
- mPkg.userId, mPkg.packageName)) {
+ nowElapsed, debits, pendingReward)) {
mStateChangedListener.onControllerStateChanged(
- mTrackedJobs.get(mPkg.userId, mPkg.packageName));
+ maybeUpdateConstraintForPkgLocked(nowElapsed,
+ mPkg.userId, mPkg.packageName));
}
}
break;
@@ -2356,11 +2522,9 @@ public final class QuotaController extends StateController {
if (timer != null && timer.isActive()) {
timer.rescheduleCutoff();
}
- if (maybeUpdateConstraintForPkgLocked(sElapsedRealtimeClock.millis(),
- userId, packageName)) {
- mStateChangedListener
- .onControllerStateChanged(mTrackedJobs.get(userId, packageName));
- }
+ mStateChangedListener.onControllerStateChanged(
+ maybeUpdateConstraintForPkgLocked(
+ sElapsedRealtimeClock.millis(), userId, packageName));
}
if (restrictedChanges.size() > 0) {
mStateChangedListener.onRestrictedBucketChanged(restrictedChanges);
@@ -2486,27 +2650,19 @@ public final class QuotaController extends StateController {
Slog.d(TAG, "Checking if " + pkg + " has reached its quota.");
}
- long timeRemainingMs = getRemainingExecutionTimeLocked(pkg.userId,
- pkg.packageName);
- if (timeRemainingMs <= 50) {
- // Less than 50 milliseconds left. Start process of shutting down jobs.
+ final ArraySet<JobStatus> changedJobs = maybeUpdateConstraintForPkgLocked(
+ sElapsedRealtimeClock.millis(), pkg.userId, pkg.packageName);
+ if (changedJobs.size() > 0) {
if (DEBUG) Slog.d(TAG, pkg + " has reached its quota.");
- if (maybeUpdateConstraintForPkgLocked(sElapsedRealtimeClock.millis(),
- pkg.userId, pkg.packageName)) {
- mStateChangedListener.onControllerStateChanged(
- mTrackedJobs.get(pkg.userId, pkg.packageName));
- }
+ mStateChangedListener.onControllerStateChanged(changedJobs);
} else {
// This could potentially happen if an old session phases out while a
// job is currently running.
// Reschedule message
- Message rescheduleMsg = obtainMessage(MSG_REACHED_QUOTA, pkg);
- timeRemainingMs = getTimeUntilQuotaConsumedLocked(pkg.userId,
- pkg.packageName);
if (DEBUG) {
- Slog.d(TAG, pkg + " has " + timeRemainingMs + "ms left.");
+ Slog.d(TAG, pkg + " had early REACHED_QUOTA message");
}
- sendMessageDelayed(rescheduleMsg, timeRemainingMs);
+ mPkgTimers.get(pkg.userId, pkg.packageName).scheduleCutoff();
}
break;
}
@@ -2516,26 +2672,19 @@ public final class QuotaController extends StateController {
Slog.d(TAG, "Checking if " + pkg + " has reached its EJ quota.");
}
- long timeRemainingMs = getRemainingEJExecutionTimeLocked(
- pkg.userId, pkg.packageName);
- if (timeRemainingMs <= 0) {
+ final ArraySet<JobStatus> changedJobs = maybeUpdateConstraintForPkgLocked(
+ sElapsedRealtimeClock.millis(), pkg.userId, pkg.packageName);
+ if (changedJobs.size() > 0) {
if (DEBUG) Slog.d(TAG, pkg + " has reached its EJ quota.");
- if (maybeUpdateConstraintForPkgLocked(sElapsedRealtimeClock.millis(),
- pkg.userId, pkg.packageName)) {
- mStateChangedListener.onControllerStateChanged(
- mTrackedJobs.get(pkg.userId, pkg.packageName));
- }
+ mStateChangedListener.onControllerStateChanged(changedJobs);
} else {
// This could potentially happen if an old session phases out while a
// job is currently running.
// Reschedule message
- Message rescheduleMsg = obtainMessage(MSG_REACHED_EJ_QUOTA, pkg);
- timeRemainingMs = getTimeUntilEJQuotaConsumedLocked(
- pkg.userId, pkg.packageName);
if (DEBUG) {
- Slog.d(TAG, pkg + " has " + timeRemainingMs + "ms left for EJ");
+ Slog.d(TAG, pkg + " had early REACHED_EJ_QUOTA message");
}
- sendMessageDelayed(rescheduleMsg, timeRemainingMs);
+ mEJPkgTimers.get(pkg.userId, pkg.packageName).scheduleCutoff();
}
break;
}
@@ -2553,11 +2702,9 @@ public final class QuotaController extends StateController {
if (DEBUG) {
Slog.d(TAG, "Checking pkg " + packageToString(userId, packageName));
}
- if (maybeUpdateConstraintForPkgLocked(sElapsedRealtimeClock.millis(),
- userId, packageName)) {
- mStateChangedListener.onControllerStateChanged(
- mTrackedJobs.get(userId, packageName));
- }
+ mStateChangedListener.onControllerStateChanged(
+ maybeUpdateConstraintForPkgLocked(sElapsedRealtimeClock.millis(),
+ userId, packageName));
break;
}
case MSG_UID_PROCESS_STATE_CHANGED: {
@@ -2781,6 +2928,12 @@ public final class QuotaController extends StateController {
static final String KEY_IN_QUOTA_BUFFER_MS =
QC_CONSTANT_PREFIX + "in_quota_buffer_ms";
@VisibleForTesting
+ static final String KEY_ALLOWED_TIME_SURPLUS_PRIORITY_LOW =
+ QC_CONSTANT_PREFIX + "allowed_time_surplus_priority_low";
+ @VisibleForTesting
+ static final String KEY_ALLOWED_TIME_SURPLUS_PRIORITY_MIN =
+ QC_CONSTANT_PREFIX + "allowed_time_surplus_priority_min";
+ @VisibleForTesting
static final String KEY_WINDOW_SIZE_ACTIVE_MS =
QC_CONSTANT_PREFIX + "window_size_active_ms";
@VisibleForTesting
@@ -2890,6 +3043,8 @@ public final class QuotaController extends StateController {
10 * 60 * 1000L; // 10 minutes
private static final long DEFAULT_IN_QUOTA_BUFFER_MS =
30 * 1000L; // 30 seconds
+ private static final float DEFAULT_ALLOWED_TIME_SURPLUS_PRIORITY_LOW = .25f;
+ private static final float DEFAULT_ALLOWED_TIME_SURPLUS_PRIORITY_MIN = .5f;
private static final long DEFAULT_WINDOW_SIZE_ACTIVE_MS =
DEFAULT_ALLOWED_TIME_PER_PERIOD_MS; // ACTIVE apps can run jobs at any time
private static final long DEFAULT_WINDOW_SIZE_WORKING_MS =
@@ -2951,6 +3106,22 @@ public final class QuotaController extends StateController {
public long IN_QUOTA_BUFFER_MS = DEFAULT_IN_QUOTA_BUFFER_MS;
/**
+ * The percentage of {@link #ALLOWED_TIME_PER_PERIOD_MS} that should not be used by
+ * {@link JobInfo#PRIORITY_LOW low priority} jobs. In other words, there must be a minimum
+ * surplus of this amount of remaining allowed time before we start running low priority
+ * jobs.
+ */
+ public float ALLOWED_TIME_SURPLUS_PRIORITY_LOW = DEFAULT_ALLOWED_TIME_SURPLUS_PRIORITY_LOW;
+
+ /**
+ * The percentage of {@link #ALLOWED_TIME_PER_PERIOD_MS} that should not be used by
+ * {@link JobInfo#PRIORITY_MIN low priority} jobs. In other words, there must be a minimum
+ * surplus of this amount of remaining allowed time before we start running min priority
+ * jobs.
+ */
+ public float ALLOWED_TIME_SURPLUS_PRIORITY_MIN = DEFAULT_ALLOWED_TIME_SURPLUS_PRIORITY_MIN;
+
+ /**
* The quota window size of the particular standby bucket. Apps in this standby bucket are
* expected to run only {@link #ALLOWED_TIME_PER_PERIOD_MS} within the past
* WINDOW_SIZE_MS.
@@ -3188,6 +3359,8 @@ public final class QuotaController extends StateController {
@NonNull String key) {
switch (key) {
case KEY_ALLOWED_TIME_PER_PERIOD_MS:
+ case KEY_ALLOWED_TIME_SURPLUS_PRIORITY_LOW:
+ case KEY_ALLOWED_TIME_SURPLUS_PRIORITY_MIN:
case KEY_IN_QUOTA_BUFFER_MS:
case KEY_MAX_EXECUTION_TIME_MS:
case KEY_WINDOW_SIZE_ACTIVE_MS:
@@ -3407,6 +3580,7 @@ public final class QuotaController extends StateController {
final DeviceConfig.Properties properties = DeviceConfig.getProperties(
DeviceConfig.NAMESPACE_JOB_SCHEDULER,
KEY_ALLOWED_TIME_PER_PERIOD_MS, KEY_IN_QUOTA_BUFFER_MS,
+ KEY_ALLOWED_TIME_SURPLUS_PRIORITY_LOW, KEY_ALLOWED_TIME_SURPLUS_PRIORITY_MIN,
KEY_MAX_EXECUTION_TIME_MS, KEY_WINDOW_SIZE_ACTIVE_MS,
KEY_WINDOW_SIZE_WORKING_MS,
KEY_WINDOW_SIZE_FREQUENT_MS, KEY_WINDOW_SIZE_RARE_MS,
@@ -3414,6 +3588,12 @@ public final class QuotaController extends StateController {
ALLOWED_TIME_PER_PERIOD_MS =
properties.getLong(KEY_ALLOWED_TIME_PER_PERIOD_MS,
DEFAULT_ALLOWED_TIME_PER_PERIOD_MS);
+ ALLOWED_TIME_SURPLUS_PRIORITY_LOW =
+ properties.getFloat(KEY_ALLOWED_TIME_SURPLUS_PRIORITY_LOW,
+ DEFAULT_ALLOWED_TIME_SURPLUS_PRIORITY_LOW);
+ ALLOWED_TIME_SURPLUS_PRIORITY_MIN =
+ properties.getFloat(KEY_ALLOWED_TIME_SURPLUS_PRIORITY_MIN,
+ DEFAULT_ALLOWED_TIME_SURPLUS_PRIORITY_MIN);
IN_QUOTA_BUFFER_MS = properties.getLong(KEY_IN_QUOTA_BUFFER_MS,
DEFAULT_IN_QUOTA_BUFFER_MS);
MAX_EXECUTION_TIME_MS = properties.getLong(KEY_MAX_EXECUTION_TIME_MS,
@@ -3455,6 +3635,23 @@ public final class QuotaController extends StateController {
mMaxExecutionTimeIntoQuotaMs = mMaxExecutionTimeMs - mQuotaBufferMs;
mShouldReevaluateConstraints = true;
}
+ // Low priority surplus should be in the range [0, .9]. A value of 1 would essentially
+ // mean never run low priority jobs.
+ float newAllowedTimeSurplusPriorityLow =
+ Math.max(0f, Math.min(.9f, ALLOWED_TIME_SURPLUS_PRIORITY_LOW));
+ if (Float.compare(
+ mAllowedTimeSurplusPriorityLow, newAllowedTimeSurplusPriorityLow) != 0) {
+ mAllowedTimeSurplusPriorityLow = newAllowedTimeSurplusPriorityLow;
+ mShouldReevaluateConstraints = true;
+ }
+ // Min priority surplus should be in the range [0, mAllowedTimeSurplusPriorityLow].
+ float newAllowedTimeSurplusPriorityMin = Math.max(0f,
+ Math.min(mAllowedTimeSurplusPriorityLow, ALLOWED_TIME_SURPLUS_PRIORITY_MIN));
+ if (Float.compare(
+ mAllowedTimeSurplusPriorityMin, newAllowedTimeSurplusPriorityMin) != 0) {
+ mAllowedTimeSurplusPriorityMin = newAllowedTimeSurplusPriorityMin;
+ mShouldReevaluateConstraints = true;
+ }
long newActivePeriodMs = Math.max(mAllowedTimePerPeriodMs,
Math.min(MAX_PERIOD_MS, WINDOW_SIZE_ACTIVE_MS));
if (mBucketPeriodsMs[ACTIVE_INDEX] != newActivePeriodMs) {
@@ -3627,6 +3824,10 @@ public final class QuotaController extends StateController {
pw.println("QuotaController:");
pw.increaseIndent();
pw.print(KEY_ALLOWED_TIME_PER_PERIOD_MS, ALLOWED_TIME_PER_PERIOD_MS).println();
+ pw.print(KEY_ALLOWED_TIME_SURPLUS_PRIORITY_LOW, ALLOWED_TIME_SURPLUS_PRIORITY_LOW)
+ .println();
+ pw.print(KEY_ALLOWED_TIME_SURPLUS_PRIORITY_MIN, ALLOWED_TIME_SURPLUS_PRIORITY_MIN)
+ .println();
pw.print(KEY_IN_QUOTA_BUFFER_MS, IN_QUOTA_BUFFER_MS).println();
pw.print(KEY_WINDOW_SIZE_ACTIVE_MS, WINDOW_SIZE_ACTIVE_MS).println();
pw.print(KEY_WINDOW_SIZE_WORKING_MS, WINDOW_SIZE_WORKING_MS).println();
@@ -3750,6 +3951,16 @@ public final class QuotaController extends StateController {
}
@VisibleForTesting
+ float getAllowedTimeSurplusPriorityLow() {
+ return mAllowedTimeSurplusPriorityLow;
+ }
+
+ @VisibleForTesting
+ float getAllowedTimeSurplusPriorityMin() {
+ return mAllowedTimeSurplusPriorityMin;
+ }
+
+ @VisibleForTesting
@NonNull
int[] getBucketMaxJobCounts() {
return mMaxBucketJobCounts;
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 8b175127373a..dd102bdd726e 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
@@ -42,6 +42,7 @@ import android.util.AtomicFile;
import android.util.IndentingPrintWriter;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseLongArray;
import android.util.TimeUtils;
import android.util.Xml;
@@ -49,6 +50,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.CollectionUtils;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.FrameworkStatsLog;
+import com.android.internal.util.XmlUtils;
import libcore.io.IoUtils;
@@ -80,7 +82,7 @@ public class AppIdleHistory {
private SparseArray<ArrayMap<String,AppUsageHistory>> mIdleHistory = new SparseArray<>();
private static final long ONE_MINUTE = 60 * 1000;
- private static final int STANDBY_BUCKET_UNKNOWN = -1;
+ static final int STANDBY_BUCKET_UNKNOWN = -1;
/**
* The bucket beyond which apps are considered idle. Any apps in this bucket or lower are
@@ -88,10 +90,35 @@ public class AppIdleHistory {
*/
static final int IDLE_BUCKET_CUTOFF = STANDBY_BUCKET_RARE;
+ /** Initial version of the xml containing the app idle stats ({@link #APP_IDLE_FILENAME}). */
+ private static final int XML_VERSION_INITIAL = 0;
+ /**
+ * Allowed writing expiry times for any standby bucket instead of only active and working set.
+ * In previous version, we used to specify expiry times for active and working set as
+ * attributes:
+ * <pre>
+ * <package activeTimeoutTime="..." workingSetTimeoutTime="..." />
+ * </pre>
+ * In this version, it is changed to:
+ * <pre>
+ * <package>
+ * <expiryTimes>
+ * <item bucket="..." expiry="..." />
+ * <item bucket="..." expiry="..." />
+ * </expiryTimes>
+ * </package>
+ * </pre>
+ */
+ private static final int XML_VERSION_ADD_BUCKET_EXPIRY_TIMES = 1;
+ /** Current version */
+ private static final int XML_VERSION_CURRENT = XML_VERSION_ADD_BUCKET_EXPIRY_TIMES;
+
@VisibleForTesting
static final String APP_IDLE_FILENAME = "app_idle_stats.xml";
private static final String TAG_PACKAGES = "packages";
private static final String TAG_PACKAGE = "package";
+ private static final String TAG_BUCKET_EXPIRY_TIMES = "expiryTimes";
+ private static final String TAG_ITEM = "item";
private static final String ATTR_NAME = "name";
// Screen on timebase time when app was last used
private static final String ATTR_SCREEN_IDLE = "screenIdleTime";
@@ -111,6 +138,10 @@ public class AppIdleHistory {
private static final String ATTR_BUCKET_ACTIVE_TIMEOUT_TIME = "activeTimeoutTime";
// The time when the forced working_set state can be overridden.
private static final String ATTR_BUCKET_WORKING_SET_TIMEOUT_TIME = "workingSetTimeoutTime";
+ // The standby bucket value
+ private static final String ATTR_BUCKET = "bucket";
+ // The time when the forced bucket state can be overridde.
+ private static final String ATTR_EXPIRY_TIME = "expiry";
// Elapsed timebase time when the app was last marked for restriction.
private static final String ATTR_LAST_RESTRICTION_ATTEMPT_ELAPSED =
"lastRestrictionAttemptElapsedTime";
@@ -119,6 +150,8 @@ public class AppIdleHistory {
"lastRestrictionAttemptReason";
// The next estimated launch time of the app, in ms since epoch.
private static final String ATTR_NEXT_ESTIMATED_APP_LAUNCH_TIME = "nextEstimatedAppLaunchTime";
+ // Version of the xml file.
+ private static final String ATTR_VERSION = "version";
// device on time = mElapsedDuration + (timeNow - mElapsedSnapshot)
private long mElapsedSnapshot; // Elapsed time snapshot when last write of mDeviceOnDuration
@@ -158,15 +191,10 @@ public class AppIdleHistory {
// The estimated time the app will be launched next, in milliseconds since epoch.
@CurrentTimeMillisLong
long nextEstimatedLaunchTime;
- // When should the bucket active state timeout, in elapsed timebase, if greater than
- // lastUsedElapsedTime.
- // This is used to keep the app in a high bucket regardless of other timeouts and
- // predictions.
- long bucketActiveTimeoutTime;
- // If there's a forced working_set state, this is when it times out. This can be sitting
- // under any active state timeout, so that it becomes applicable after the active state
- // timeout expires.
- long bucketWorkingSetTimeoutTime;
+ // Contains standby buckets that apps were forced into and the corresponding expiry times
+ // (in elapsed timebase) for each bucket state. App will stay in the highest bucket until
+ // it's expiry time is elapsed and will be moved to the next highest bucket.
+ SparseLongArray bucketExpiryTimesMs;
// The last time an agent attempted to put the app into the RESTRICTED bucket.
long lastRestrictAttemptElapsedTime;
// The last reason the app was marked to be put into the RESTRICTED bucket.
@@ -249,21 +277,24 @@ public class AppIdleHistory {
}
/**
- * Mark the app as used and update the bucket if necessary. If there is a timeout specified
+ * Mark the app as used and update the bucket if necessary. If there is a expiry time specified
* that's in the future, then the usage event is temporary and keeps the app in the specified
- * bucket at least until the timeout is reached. This can be used to keep the app in an
+ * bucket at least until the expiry time is reached. This can be used to keep the app in an
* elevated bucket for a while until some important task gets to run.
+ *
* @param appUsageHistory the usage record for the app being updated
* @param packageName name of the app being updated, for logging purposes
* @param newBucket the bucket to set the app to
* @param usageReason the sub-reason for usage, one of REASON_SUB_USAGE_*
- * @param elapsedRealtime mark as used time if non-zero
- * @param timeout set the timeout of the specified bucket, if non-zero. Can only be used
- * with bucket values of ACTIVE and WORKING_SET.
+ * @param nowElapsedRealtimeMs mark as used time if non-zero (in
+ * {@link SystemClock#elapsedRealtime()} time base)
+ * @param expiryElapsedRealtimeMs the expiry time for the specified bucket (in
+ * {@link SystemClock#elapsedRealtime()} time base)
* @return {@code appUsageHistory}
*/
AppUsageHistory reportUsage(AppUsageHistory appUsageHistory, String packageName, int userId,
- int newBucket, int usageReason, long elapsedRealtime, long timeout) {
+ int newBucket, int usageReason,
+ long nowElapsedRealtimeMs, long expiryElapsedRealtimeMs) {
int bucketingReason = REASON_MAIN_USAGE | usageReason;
final boolean isUserUsage = isUserUsage(bucketingReason);
@@ -274,30 +305,27 @@ public class AppIdleHistory {
newBucket = STANDBY_BUCKET_RESTRICTED;
bucketingReason = appUsageHistory.bucketingReason;
} else {
- // Set the timeout if applicable
- if (timeout > elapsedRealtime) {
+ // Set the expiry time if applicable
+ if (expiryElapsedRealtimeMs > nowElapsedRealtimeMs) {
// Convert to elapsed timebase
- final long timeoutTime = mElapsedDuration + (timeout - mElapsedSnapshot);
- if (newBucket == STANDBY_BUCKET_ACTIVE) {
- appUsageHistory.bucketActiveTimeoutTime = Math.max(timeoutTime,
- appUsageHistory.bucketActiveTimeoutTime);
- } else if (newBucket == STANDBY_BUCKET_WORKING_SET) {
- appUsageHistory.bucketWorkingSetTimeoutTime = Math.max(timeoutTime,
- appUsageHistory.bucketWorkingSetTimeoutTime);
- } else {
- throw new IllegalArgumentException("Cannot set a timeout on bucket="
- + newBucket);
+ final long expiryTimeMs = getElapsedTime(expiryElapsedRealtimeMs);
+ if (appUsageHistory.bucketExpiryTimesMs == null) {
+ appUsageHistory.bucketExpiryTimesMs = new SparseLongArray();
}
+ final long currentExpiryTimeMs = appUsageHistory.bucketExpiryTimesMs.get(newBucket);
+ appUsageHistory.bucketExpiryTimesMs.put(newBucket,
+ Math.max(expiryTimeMs, currentExpiryTimeMs));
+ removeElapsedExpiryTimes(appUsageHistory, getElapsedTime(nowElapsedRealtimeMs));
}
}
- if (elapsedRealtime != 0) {
+ if (nowElapsedRealtimeMs != 0) {
appUsageHistory.lastUsedElapsedTime = mElapsedDuration
- + (elapsedRealtime - mElapsedSnapshot);
+ + (nowElapsedRealtimeMs - mElapsedSnapshot);
if (isUserUsage) {
appUsageHistory.lastUsedByUserElapsedTime = appUsageHistory.lastUsedElapsedTime;
}
- appUsageHistory.lastUsedScreenTime = getScreenOnTime(elapsedRealtime);
+ appUsageHistory.lastUsedScreenTime = getScreenOnTime(nowElapsedRealtimeMs);
}
if (appUsageHistory.currentBucket > newBucket) {
@@ -309,26 +337,41 @@ public class AppIdleHistory {
return appUsageHistory;
}
+ private void removeElapsedExpiryTimes(AppUsageHistory appUsageHistory, long elapsedTimeMs) {
+ if (appUsageHistory.bucketExpiryTimesMs == null) {
+ return;
+ }
+ for (int i = appUsageHistory.bucketExpiryTimesMs.size() - 1; i >= 0; --i) {
+ if (appUsageHistory.bucketExpiryTimesMs.valueAt(i) < elapsedTimeMs) {
+ appUsageHistory.bucketExpiryTimesMs.removeAt(i);
+ }
+ }
+ }
+
/**
- * Mark the app as used and update the bucket if necessary. If there is a timeout specified
+ * Mark the app as used and update the bucket if necessary. If there is a expiry time specified
* that's in the future, then the usage event is temporary and keeps the app in the specified
- * bucket at least until the timeout is reached. This can be used to keep the app in an
+ * bucket at least until the expiry time is reached. This can be used to keep the app in an
* elevated bucket for a while until some important task gets to run.
- * @param packageName
- * @param userId
+ *
+ * @param packageName package name of the app the usage is reported for
+ * @param userId user that the app is running in
* @param newBucket the bucket to set the app to
* @param usageReason sub reason for usage
- * @param nowElapsed mark as used time if non-zero
- * @param timeout set the timeout of the specified bucket, if non-zero. Can only be used
- * with bucket values of ACTIVE and WORKING_SET.
- * @return
+ * @param nowElapsedRealtimeMs mark as used time if non-zero (in
+ * {@link SystemClock#elapsedRealtime()} time base).
+ * @param expiryElapsedRealtimeMs the expiry time for the specified bucket (in
+ * {@link SystemClock#elapsedRealtime()} time base).
+ * @return the {@link AppUsageHistory} corresponding to the {@code packageName}
+ * and {@code userId}.
*/
public AppUsageHistory reportUsage(String packageName, int userId, int newBucket,
- int usageReason, long nowElapsed, long timeout) {
+ int usageReason, long nowElapsedRealtimeMs, long expiryElapsedRealtimeMs) {
ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
- AppUsageHistory history = getPackageHistory(userHistory, packageName, nowElapsed, true);
- return reportUsage(history, packageName, userId, newBucket, usageReason, nowElapsed,
- timeout);
+ AppUsageHistory history = getPackageHistory(userHistory, packageName,
+ nowElapsedRealtimeMs, true);
+ return reportUsage(history, packageName, userId, newBucket, usageReason,
+ nowElapsedRealtimeMs, expiryElapsedRealtimeMs);
}
private ArrayMap<String, AppUsageHistory> getUserHistory(int userId) {
@@ -383,7 +426,7 @@ public class AppIdleHistory {
}
public void setAppStandbyBucket(String packageName, int userId, long elapsedRealtime,
- int bucket, int reason, boolean resetTimeout) {
+ int bucket, int reason, boolean resetExpiryTimes) {
ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
AppUsageHistory appUsageHistory =
getPackageHistory(userHistory, packageName, elapsedRealtime, true);
@@ -397,9 +440,8 @@ public class AppIdleHistory {
appUsageHistory.lastPredictedTime = elapsed;
appUsageHistory.lastPredictedBucket = bucket;
}
- if (resetTimeout) {
- appUsageHistory.bucketActiveTimeoutTime = elapsed;
- appUsageHistory.bucketWorkingSetTimeoutTime = elapsed;
+ if (resetExpiryTimes && appUsageHistory.bucketExpiryTimesMs != null) {
+ appUsageHistory.bucketExpiryTimesMs.clear();
}
if (changed) {
logAppStandbyBucketChanged(packageName, userId, bucket, reason);
@@ -622,6 +664,17 @@ public class AppIdleHistory {
}
@VisibleForTesting
+ long getBucketExpiryTimeMs(String packageName, int userId, int bucket, long elapsedRealtimeMs) {
+ ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
+ AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName,
+ elapsedRealtimeMs, true);
+ if (appUsageHistory.bucketExpiryTimesMs == null) {
+ return 0;
+ }
+ return appUsageHistory.bucketExpiryTimesMs.get(bucket, 0);
+ }
+
+ @VisibleForTesting
File getUserFile(int userId) {
return new File(new File(new File(mStorageDir, "users"),
Integer.toString(userId)), APP_IDLE_FILENAME);
@@ -657,6 +710,7 @@ public class AppIdleHistory {
if (!parser.getName().equals(TAG_PACKAGES)) {
return;
}
+ final int version = getIntValue(parser, ATTR_VERSION, XML_VERSION_INITIAL);
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
if (type == XmlPullParser.START_TAG) {
final String name = parser.getName();
@@ -681,10 +735,6 @@ public class AppIdleHistory {
parser.getAttributeValue(null, ATTR_BUCKETING_REASON);
appUsageHistory.lastJobRunTime = getLongValue(parser,
ATTR_LAST_RUN_JOB_TIME, Long.MIN_VALUE);
- appUsageHistory.bucketActiveTimeoutTime = getLongValue(parser,
- ATTR_BUCKET_ACTIVE_TIMEOUT_TIME, 0L);
- appUsageHistory.bucketWorkingSetTimeoutTime = getLongValue(parser,
- ATTR_BUCKET_WORKING_SET_TIMEOUT_TIME, 0L);
appUsageHistory.bucketingReason = REASON_MAIN_DEFAULT;
if (bucketingReason != null) {
try {
@@ -710,6 +760,26 @@ public class AppIdleHistory {
ATTR_NEXT_ESTIMATED_APP_LAUNCH_TIME, 0);
appUsageHistory.lastInformedBucket = -1;
userHistory.put(packageName, appUsageHistory);
+
+ if (version >= XML_VERSION_ADD_BUCKET_EXPIRY_TIMES) {
+ final int outerDepth = parser.getDepth();
+ while (XmlUtils.nextElementWithin(parser, outerDepth)) {
+ if (TAG_BUCKET_EXPIRY_TIMES.equals(parser.getName())) {
+ readBucketExpiryTimes(parser, appUsageHistory);
+ }
+ }
+ } else {
+ final long bucketActiveTimeoutTime = getLongValue(parser,
+ ATTR_BUCKET_ACTIVE_TIMEOUT_TIME, 0L);
+ final long bucketWorkingSetTimeoutTime = getLongValue(parser,
+ ATTR_BUCKET_WORKING_SET_TIMEOUT_TIME, 0L);
+ if (bucketActiveTimeoutTime != 0 || bucketWorkingSetTimeoutTime != 0) {
+ insertBucketExpiryTime(appUsageHistory,
+ STANDBY_BUCKET_ACTIVE, bucketActiveTimeoutTime);
+ insertBucketExpiryTime(appUsageHistory,
+ STANDBY_BUCKET_WORKING_SET, bucketWorkingSetTimeoutTime);
+ }
+ }
}
}
}
@@ -720,21 +790,53 @@ public class AppIdleHistory {
}
}
+ private void readBucketExpiryTimes(XmlPullParser parser, AppUsageHistory appUsageHistory)
+ throws IOException, XmlPullParserException {
+ final int depth = parser.getDepth();
+ while (XmlUtils.nextElementWithin(parser, depth)) {
+ if (TAG_ITEM.equals(parser.getName())) {
+ final int bucket = getIntValue(parser, ATTR_BUCKET, STANDBY_BUCKET_UNKNOWN);
+ if (bucket == STANDBY_BUCKET_UNKNOWN) {
+ Slog.e(TAG, "Error reading the buckets expiry times");
+ continue;
+ }
+ final long expiryTimeMs = getLongValue(parser, ATTR_EXPIRY_TIME, 0 /* default */);
+ insertBucketExpiryTime(appUsageHistory, bucket, expiryTimeMs);
+ }
+ }
+ }
+
+ private void insertBucketExpiryTime(AppUsageHistory appUsageHistory,
+ int bucket, long expiryTimeMs) {
+ if (expiryTimeMs == 0) {
+ return;
+ }
+ if (appUsageHistory.bucketExpiryTimesMs == null) {
+ appUsageHistory.bucketExpiryTimesMs = new SparseLongArray();
+ }
+ appUsageHistory.bucketExpiryTimesMs.put(bucket, expiryTimeMs);
+ }
+
private long getLongValue(XmlPullParser parser, String attrName, long defValue) {
String value = parser.getAttributeValue(null, attrName);
if (value == null) return defValue;
return Long.parseLong(value);
}
+ private int getIntValue(XmlPullParser parser, String attrName, int defValue) {
+ String value = parser.getAttributeValue(null, attrName);
+ if (value == null) return defValue;
+ return Integer.parseInt(value);
+ }
- public void writeAppIdleTimes() {
+ public void writeAppIdleTimes(long elapsedRealtimeMs) {
final int size = mIdleHistory.size();
for (int i = 0; i < size; i++) {
- writeAppIdleTimes(mIdleHistory.keyAt(i));
+ writeAppIdleTimes(mIdleHistory.keyAt(i), elapsedRealtimeMs);
}
}
- public void writeAppIdleTimes(int userId) {
+ public void writeAppIdleTimes(int userId, long elapsedRealtimeMs) {
FileOutputStream fos = null;
AtomicFile appIdleFile = new AtomicFile(getUserFile(userId));
try {
@@ -747,7 +849,9 @@ public class AppIdleHistory {
xml.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
xml.startTag(null, TAG_PACKAGES);
+ xml.attribute(null, ATTR_VERSION, String.valueOf(XML_VERSION_CURRENT));
+ final long elapsedTimeMs = getElapsedTime(elapsedRealtimeMs);
ArrayMap<String,AppUsageHistory> userHistory = getUserHistory(userId);
final int N = userHistory.size();
for (int i = 0; i < N; i++) {
@@ -772,14 +876,6 @@ public class AppIdleHistory {
Integer.toString(history.currentBucket));
xml.attribute(null, ATTR_BUCKETING_REASON,
Integer.toHexString(history.bucketingReason));
- if (history.bucketActiveTimeoutTime > 0) {
- xml.attribute(null, ATTR_BUCKET_ACTIVE_TIMEOUT_TIME, Long.toString(history
- .bucketActiveTimeoutTime));
- }
- if (history.bucketWorkingSetTimeoutTime > 0) {
- xml.attribute(null, ATTR_BUCKET_WORKING_SET_TIMEOUT_TIME, Long.toString(history
- .bucketWorkingSetTimeoutTime));
- }
if (history.lastJobRunTime != Long.MIN_VALUE) {
xml.attribute(null, ATTR_LAST_RUN_JOB_TIME, Long.toString(history
.lastJobRunTime));
@@ -794,6 +890,23 @@ public class AppIdleHistory {
xml.attribute(null, ATTR_NEXT_ESTIMATED_APP_LAUNCH_TIME,
Long.toString(history.nextEstimatedLaunchTime));
}
+ if (history.bucketExpiryTimesMs != null) {
+ xml.startTag(null, TAG_BUCKET_EXPIRY_TIMES);
+ final int size = history.bucketExpiryTimesMs.size();
+ for (int j = 0; j < size; ++j) {
+ final long expiryTimeMs = history.bucketExpiryTimesMs.valueAt(j);
+ // Skip writing to disk if the expiry time already elapsed.
+ if (expiryTimeMs < elapsedTimeMs) {
+ continue;
+ }
+ final int bucket = history.bucketExpiryTimesMs.keyAt(j);
+ xml.startTag(null, TAG_ITEM);
+ xml.attribute(null, ATTR_BUCKET, String.valueOf(bucket));
+ xml.attribute(null, ATTR_EXPIRY_TIME, String.valueOf(expiryTimeMs));
+ xml.endTag(null, TAG_ITEM);
+ }
+ xml.endTag(null, TAG_BUCKET_EXPIRY_TIMES);
+ }
xml.endTag(null, TAG_PACKAGE);
}
@@ -846,12 +959,7 @@ public class AppIdleHistory {
TimeUtils.formatDuration(screenOnTime - appUsageHistory.lastUsedScreenTime, idpw);
idpw.print(" lastPred=");
TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastPredictedTime, idpw);
- idpw.print(" activeLeft=");
- TimeUtils.formatDuration(appUsageHistory.bucketActiveTimeoutTime - totalElapsedTime,
- idpw);
- idpw.print(" wsLeft=");
- TimeUtils.formatDuration(appUsageHistory.bucketWorkingSetTimeoutTime - totalElapsedTime,
- idpw);
+ dumpBucketExpiryTimes(idpw, appUsageHistory, totalElapsedTime);
idpw.print(" lastJob=");
TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastJobRunTime, idpw);
if (appUsageHistory.lastRestrictAttemptElapsedTime > 0) {
@@ -877,4 +985,26 @@ public class AppIdleHistory {
idpw.println();
idpw.decreaseIndent();
}
+
+ private void dumpBucketExpiryTimes(IndentingPrintWriter idpw, AppUsageHistory appUsageHistory,
+ long totalElapsedTimeMs) {
+ idpw.print(" expiryTimes=");
+ if (appUsageHistory.bucketExpiryTimesMs == null
+ || appUsageHistory.bucketExpiryTimesMs.size() == 0) {
+ idpw.print("<none>");
+ return;
+ }
+ idpw.print("(");
+ final int size = appUsageHistory.bucketExpiryTimesMs.size();
+ for (int i = 0; i < size; ++i) {
+ final int bucket = appUsageHistory.bucketExpiryTimesMs.keyAt(i);
+ final long expiryTimeMs = appUsageHistory.bucketExpiryTimesMs.valueAt(i);
+ if (i != 0) {
+ idpw.print(",");
+ }
+ idpw.print(bucket + ":");
+ TimeUtils.formatDuration(totalElapsedTimeMs - expiryTimeMs, idpw);
+ }
+ idpw.print(")");
+ }
}
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 abbae4e8e43c..0ad70e40de7f 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -49,10 +49,12 @@ import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_NEVER;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RESTRICTED;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
+import static android.app.usage.UsageStatsManager.standbyBucketToString;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static com.android.server.SystemService.PHASE_BOOT_COMPLETED;
import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY;
+import static com.android.server.usage.AppIdleHistory.STANDBY_BUCKET_UNKNOWN;
import android.annotation.CurrentTimeMillisLong;
import android.annotation.NonNull;
@@ -181,7 +183,7 @@ public class AppStandbyController
COMPRESS_TIME ? 1 * ONE_MINUTE : 12 * ONE_HOUR,
COMPRESS_TIME ? 4 * ONE_MINUTE : 24 * ONE_HOUR,
COMPRESS_TIME ? 16 * ONE_MINUTE : 48 * ONE_HOUR,
- COMPRESS_TIME ? 32 * ONE_MINUTE : 45 * ONE_DAY
+ COMPRESS_TIME ? 32 * ONE_MINUTE : 3 * ONE_DAY
};
/** The minimum allowed values for each index in {@link #DEFAULT_ELAPSED_TIME_THRESHOLDS}. */
@@ -298,6 +300,11 @@ public class AppStandbyController
long mStrongUsageTimeoutMillis = ConstantsObserver.DEFAULT_STRONG_USAGE_TIMEOUT;
/** Minimum time a notification seen event should keep the bucket elevated. */
long mNotificationSeenTimeoutMillis = ConstantsObserver.DEFAULT_NOTIFICATION_TIMEOUT;
+ /** Minimum time a slice pinned event should keep the bucket elevated. */
+ long mSlicePinnedTimeoutMillis = ConstantsObserver.DEFAULT_SLICE_PINNED_TIMEOUT;
+ /** The standby bucket that an app will be promoted on a notification-seen event */
+ int mNotificationSeenPromotedBucket =
+ ConstantsObserver.DEFAULT_NOTIFICATION_SEEN_PROMOTED_BUCKET;
/** Minimum time a system update event should keep the buckets elevated. */
long mSystemUpdateUsageTimeoutMillis = ConstantsObserver.DEFAULT_SYSTEM_UPDATE_TIMEOUT;
/** Maximum time to wait for a prediction before using simple timeouts to downgrade buckets. */
@@ -773,7 +780,7 @@ public class AppStandbyController
userId);
if (DEBUG) {
Slog.d(TAG, " Checking idle state for " + packageName
- + " minBucket=" + minBucket);
+ + " minBucket=" + standbyBucketToString(minBucket));
}
if (minBucket <= STANDBY_BUCKET_ACTIVE) {
// No extra processing needed for ACTIVE or higher since apps can't drop into lower
@@ -815,36 +822,34 @@ public class AppStandbyController
newBucket = app.lastPredictedBucket;
reason = REASON_MAIN_PREDICTED | REASON_SUB_PREDICTED_RESTORED;
if (DEBUG) {
- Slog.d(TAG, "Restored predicted newBucket = " + newBucket);
+ Slog.d(TAG, "Restored predicted newBucket = "
+ + standbyBucketToString(newBucket));
}
} else {
newBucket = getBucketForLocked(packageName, userId,
elapsedRealtime);
if (DEBUG) {
- Slog.d(TAG, "Evaluated AOSP newBucket = " + newBucket);
+ Slog.d(TAG, "Evaluated AOSP newBucket = "
+ + standbyBucketToString(newBucket));
}
reason = REASON_MAIN_TIMEOUT;
}
}
- // Check if the app is within one of the timeouts for forced bucket elevation
+ // Check if the app is within one of the expiry times for forced bucket elevation
final long elapsedTimeAdjusted = mAppIdleHistory.getElapsedTime(elapsedRealtime);
- if (newBucket >= STANDBY_BUCKET_ACTIVE
- && app.bucketActiveTimeoutTime > elapsedTimeAdjusted) {
- newBucket = STANDBY_BUCKET_ACTIVE;
- reason = app.bucketingReason;
- if (DEBUG) {
- Slog.d(TAG, " Keeping at ACTIVE due to min timeout");
+ final int bucketWithValidExpiryTime = getMinBucketWithValidExpiryTime(app,
+ newBucket, elapsedTimeAdjusted);
+ if (bucketWithValidExpiryTime != STANDBY_BUCKET_UNKNOWN) {
+ newBucket = bucketWithValidExpiryTime;
+ if (newBucket == STANDBY_BUCKET_ACTIVE || app.currentBucket == newBucket) {
+ reason = app.bucketingReason;
+ } else {
+ reason = REASON_MAIN_USAGE | REASON_SUB_USAGE_ACTIVE_TIMEOUT;
}
- } else if (newBucket >= STANDBY_BUCKET_WORKING_SET
- && app.bucketWorkingSetTimeoutTime > elapsedTimeAdjusted) {
- newBucket = STANDBY_BUCKET_WORKING_SET;
- // If it was already there, keep the reason, else assume timeout to WS
- reason = (newBucket == oldBucket)
- ? app.bucketingReason
- : REASON_MAIN_USAGE | REASON_SUB_USAGE_ACTIVE_TIMEOUT;
if (DEBUG) {
- Slog.d(TAG, " Keeping at WORKING_SET due to min timeout");
+ Slog.d(TAG, " Keeping at " + standbyBucketToString(newBucket)
+ + " due to min timeout");
}
}
@@ -868,13 +873,14 @@ public class AppStandbyController
newBucket = minBucket;
// Leave the reason alone.
if (DEBUG) {
- Slog.d(TAG, "Bringing up from " + newBucket + " to " + minBucket
+ Slog.d(TAG, "Bringing up from " + standbyBucketToString(newBucket)
+ + " to " + standbyBucketToString(minBucket)
+ " due to min bucketing");
}
}
if (DEBUG) {
- Slog.d(TAG, " Old bucket=" + oldBucket
- + ", newBucket=" + newBucket);
+ Slog.d(TAG, " Old bucket=" + standbyBucketToString(oldBucket)
+ + ", newBucket=" + standbyBucketToString(newBucket));
}
if (oldBucket != newBucket || predictionLate) {
mAppIdleHistory.setAppStandbyBucket(packageName, userId,
@@ -967,6 +973,7 @@ public class AppStandbyController
}
}
+ @GuardedBy("mAppIdleLock")
private void reportEventLocked(String pkg, int eventType, long elapsedRealtime, int userId) {
// TODO: Ideally this should call isAppIdleFiltered() to avoid calling back
// about apps that are on some kind of whitelist anyway.
@@ -980,13 +987,20 @@ public class AppStandbyController
final long nextCheckDelay;
final int subReason = usageEventToSubReason(eventType);
final int reason = REASON_MAIN_USAGE | subReason;
- if (eventType == UsageEvents.Event.NOTIFICATION_SEEN
- || eventType == UsageEvents.Event.SLICE_PINNED) {
- // Mild usage elevates to WORKING_SET but doesn't change usage time.
+ if (eventType == UsageEvents.Event.NOTIFICATION_SEEN) {
+ // Notification-seen elevates to a higher bucket (depending on
+ // {@link ConstantsObserver#KEY_NOTIFICATION_SEEN_PROMOTED_BUCKET}) but doesn't
+ // change usage time.
mAppIdleHistory.reportUsage(appHistory, pkg, userId,
- STANDBY_BUCKET_WORKING_SET, subReason,
+ mNotificationSeenPromotedBucket, subReason,
0, elapsedRealtime + mNotificationSeenTimeoutMillis);
nextCheckDelay = mNotificationSeenTimeoutMillis;
+ } else if (eventType == UsageEvents.Event.SLICE_PINNED) {
+ // Mild usage elevates to WORKING_SET but doesn't change usage time.
+ mAppIdleHistory.reportUsage(appHistory, pkg, userId,
+ STANDBY_BUCKET_WORKING_SET, subReason,
+ 0, elapsedRealtime + mSlicePinnedTimeoutMillis);
+ nextCheckDelay = mSlicePinnedTimeoutMillis;
} else if (eventType == UsageEvents.Event.SYSTEM_INTERACTION) {
mAppIdleHistory.reportUsage(appHistory, pkg, userId,
STANDBY_BUCKET_ACTIVE, subReason,
@@ -1022,6 +1036,29 @@ public class AppStandbyController
}
/**
+ * Returns the lowest standby bucket that is better than {@code targetBucket} and has an
+ * valid expiry time (i.e. the expiry time is not yet elapsed).
+ */
+ private int getMinBucketWithValidExpiryTime(AppUsageHistory usageHistory,
+ int targetBucket, long elapsedTimeMs) {
+ if (usageHistory.bucketExpiryTimesMs == null) {
+ return STANDBY_BUCKET_UNKNOWN;
+ }
+ final int size = usageHistory.bucketExpiryTimesMs.size();
+ for (int i = 0; i < size; ++i) {
+ final int bucket = usageHistory.bucketExpiryTimesMs.keyAt(i);
+ if (targetBucket <= bucket) {
+ break;
+ }
+ final long expiryTimeMs = usageHistory.bucketExpiryTimesMs.valueAt(i);
+ if (expiryTimeMs > elapsedTimeMs) {
+ return bucket;
+ }
+ }
+ return STANDBY_BUCKET_UNKNOWN;
+ }
+
+ /**
* Note: don't call this with the lock held since it makes calls to other system services.
*/
private @NonNull List<UserHandle> getCrossProfileTargets(String pkg, int userId) {
@@ -1564,23 +1601,18 @@ public class AppStandbyController
// ACTIVE or WORKING_SET timeout.
mAppIdleHistory.updateLastPrediction(app, elapsedTimeAdjusted, newBucket);
- if (newBucket > STANDBY_BUCKET_ACTIVE
- && app.bucketActiveTimeoutTime > elapsedTimeAdjusted) {
- newBucket = STANDBY_BUCKET_ACTIVE;
- reason = app.bucketingReason;
- if (DEBUG) {
- Slog.d(TAG, " Keeping at ACTIVE due to min timeout");
- }
- } else if (newBucket > STANDBY_BUCKET_WORKING_SET
- && app.bucketWorkingSetTimeoutTime > elapsedTimeAdjusted) {
- newBucket = STANDBY_BUCKET_WORKING_SET;
- if (app.currentBucket != newBucket) {
- reason = REASON_MAIN_USAGE | REASON_SUB_USAGE_ACTIVE_TIMEOUT;
- } else {
+ final int bucketWithValidExpiryTime = getMinBucketWithValidExpiryTime(app,
+ newBucket, elapsedTimeAdjusted);
+ if (bucketWithValidExpiryTime != STANDBY_BUCKET_UNKNOWN) {
+ newBucket = bucketWithValidExpiryTime;
+ if (newBucket == STANDBY_BUCKET_ACTIVE || app.currentBucket == newBucket) {
reason = app.bucketingReason;
+ } else {
+ reason = REASON_MAIN_USAGE | REASON_SUB_USAGE_ACTIVE_TIMEOUT;
}
if (DEBUG) {
- Slog.d(TAG, " Keeping at WORKING_SET due to min timeout");
+ Slog.d(TAG, " Keeping at " + standbyBucketToString(newBucket)
+ + " due to min timeout");
}
} else if (newBucket == STANDBY_BUCKET_RARE
&& mAllowRestrictedBucket
@@ -1746,7 +1778,7 @@ public class AppStandbyController
@Override
public void flushToDisk() {
synchronized (mAppIdleLock) {
- mAppIdleHistory.writeAppIdleTimes();
+ mAppIdleHistory.writeAppIdleTimes(mInjector.elapsedRealtime());
mAppIdleHistory.writeAppIdleDurations();
}
}
@@ -1897,7 +1929,7 @@ public class AppStandbyController
}
}
// Immediately persist defaults to disk
- mAppIdleHistory.writeAppIdleTimes(userId);
+ mAppIdleHistory.writeAppIdleTimes(userId, elapsedRealtime);
}
}
@@ -1964,6 +1996,12 @@ public class AppStandbyController
pw.print(" mNotificationSeenTimeoutMillis=");
TimeUtils.formatDuration(mNotificationSeenTimeoutMillis, pw);
pw.println();
+ pw.print(" mNotificationSeenPromotedBucket=");
+ pw.print(standbyBucketToString(mNotificationSeenPromotedBucket));
+ pw.println();
+ pw.print(" mSlicePinnedTimeoutMillis=");
+ TimeUtils.formatDuration(mSlicePinnedTimeoutMillis, pw);
+ pw.println();
pw.print(" mSyncAdapterTimeoutMillis=");
TimeUtils.formatDuration(mSyncAdapterTimeoutMillis, pw);
pw.println();
@@ -2386,6 +2424,10 @@ public class AppStandbyController
private static final String KEY_STRONG_USAGE_HOLD_DURATION = "strong_usage_duration";
private static final String KEY_NOTIFICATION_SEEN_HOLD_DURATION =
"notification_seen_duration";
+ private static final String KEY_NOTIFICATION_SEEN_PROMOTED_BUCKET =
+ "notification_seen_promoted_bucket";
+ private static final String KEY_SLICE_PINNED_HOLD_DURATION =
+ "slice_pinned_duration";
private static final String KEY_SYSTEM_UPDATE_HOLD_DURATION =
"system_update_usage_duration";
private static final String KEY_PREDICTION_TIMEOUT = "prediction_timeout";
@@ -2428,6 +2470,10 @@ public class AppStandbyController
COMPRESS_TIME ? ONE_MINUTE : 1 * ONE_HOUR;
public static final long DEFAULT_NOTIFICATION_TIMEOUT =
COMPRESS_TIME ? 12 * ONE_MINUTE : 12 * ONE_HOUR;
+ public static final long DEFAULT_SLICE_PINNED_TIMEOUT =
+ COMPRESS_TIME ? 12 * ONE_MINUTE : 12 * ONE_HOUR;
+ public static final int DEFAULT_NOTIFICATION_SEEN_PROMOTED_BUCKET =
+ STANDBY_BUCKET_WORKING_SET;
public static final long DEFAULT_SYSTEM_UPDATE_TIMEOUT =
COMPRESS_TIME ? 2 * ONE_MINUTE : 2 * ONE_HOUR;
public static final long DEFAULT_SYSTEM_INTERACTION_TIMEOUT =
@@ -2494,7 +2540,7 @@ public class AppStandbyController
switch (name) {
case KEY_AUTO_RESTRICTED_BUCKET_DELAY_MS:
mInjector.mAutoRestrictedBucketDelayMs = Math.max(
- COMPRESS_TIME ? ONE_MINUTE : 2 * ONE_HOUR,
+ COMPRESS_TIME ? ONE_MINUTE : 4 * ONE_HOUR,
properties.getLong(KEY_AUTO_RESTRICTED_BUCKET_DELAY_MS,
DEFAULT_AUTO_RESTRICTED_BUCKET_DELAY_MS));
break;
@@ -2513,6 +2559,16 @@ public class AppStandbyController
KEY_NOTIFICATION_SEEN_HOLD_DURATION,
DEFAULT_NOTIFICATION_TIMEOUT);
break;
+ case KEY_NOTIFICATION_SEEN_PROMOTED_BUCKET:
+ mNotificationSeenPromotedBucket = properties.getInt(
+ KEY_NOTIFICATION_SEEN_PROMOTED_BUCKET,
+ DEFAULT_NOTIFICATION_SEEN_PROMOTED_BUCKET);
+ break;
+ case KEY_SLICE_PINNED_HOLD_DURATION:
+ mSlicePinnedTimeoutMillis = properties.getLong(
+ KEY_SLICE_PINNED_HOLD_DURATION,
+ DEFAULT_SLICE_PINNED_TIMEOUT);
+ break;
case KEY_STRONG_USAGE_HOLD_DURATION:
mStrongUsageTimeoutMillis = properties.getLong(
KEY_STRONG_USAGE_HOLD_DURATION, DEFAULT_STRONG_USAGE_TIMEOUT);
diff --git a/apex/media/framework/java/android/media/MediaSession2Service.java b/apex/media/framework/java/android/media/MediaSession2Service.java
index f6fd509fd245..9f80c433d580 100644
--- a/apex/media/framework/java/android/media/MediaSession2Service.java
+++ b/apex/media/framework/java/android/media/MediaSession2Service.java
@@ -161,19 +161,19 @@ public abstract class MediaSession2Service extends Service {
public abstract MediaSession2 onGetSession(@NonNull ControllerInfo controllerInfo);
/**
- * Called when notification UI needs update. Override this method to show or cancel your own
- * notification UI.
+ * Called to update the media notification when the playback state changes.
* <p>
- * This would be called on {@link MediaSession2}'s callback executor when playback state is
- * changed.
+ * If playback is active and a notification is returned, the service uses it to become a
+ * foreground service. If playback is not active then the notification is still posted, but the
+ * service does not become a foreground service.
* <p>
- * With the notification returned here, the service becomes foreground service when the playback
- * is started. Apps must request the permission
- * {@link android.Manifest.permission#FOREGROUND_SERVICE} in order to use this API. It becomes
- * background service after the playback is stopped.
+ * Apps must request the {@link android.Manifest.permission#FOREGROUND_SERVICE} permission
+ * in order to use this API. For apps targeting {@link android.os.Build.VERSION_CODES#TIRAMISU}
+ * or later, notifications will only be posted if the app has also been granted the
+ * {@link android.Manifest.permission#POST_NOTIFICATIONS} permission.
*
- * @param session a session that needs notification update.
- * @return a {@link MediaNotification}. Can be {@code null}.
+ * @param session the session for which an updated media notification is required.
+ * @return the {@link MediaNotification}. Can be {@code null}.
*/
@Nullable
public abstract MediaNotification onUpdateNotification(@NonNull MediaSession2 session);
diff --git a/api/Android.bp b/api/Android.bp
index 362f39f2beaf..9428fcc41b81 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -128,11 +128,13 @@ combined_apis {
"i18n.module.public.api",
],
conditional_bootclasspath: [
+ "framework-auxiliary",
"framework-supplementalapi",
],
system_server_classpath: [
"service-media-s",
"service-permission",
+ "service-supplementalprocess",
],
}
diff --git a/boot/Android.bp b/boot/Android.bp
index 3273f2c6ed8e..8958d70f63cf 100644
--- a/boot/Android.bp
+++ b/boot/Android.bp
@@ -56,6 +56,10 @@ platform_bootclasspath {
module: "art-bootclasspath-fragment",
},
{
+ apex: "com.android.auxiliary",
+ module: "com.android.auxiliary-bootclasspath-fragment",
+ },
+ {
apex: "com.android.conscrypt",
module: "com.android.conscrypt-bootclasspath-fragment",
},
diff --git a/cmds/app_process/Android.bp b/cmds/app_process/Android.bp
index a1575173ded6..6a685a79cc33 100644
--- a/cmds/app_process/Android.bp
+++ b/cmds/app_process/Android.bp
@@ -64,6 +64,8 @@ cc_binary {
"libwilhelm",
],
+ header_libs: ["bionic_libc_platform_headers"],
+
compile_multilib: "both",
cflags: [
diff --git a/cmds/app_process/app_main.cpp b/cmds/app_process/app_main.cpp
index 12083b6fe20b..815f9455471c 100644
--- a/cmds/app_process/app_main.cpp
+++ b/cmds/app_process/app_main.cpp
@@ -15,6 +15,7 @@
#include <android-base/macros.h>
#include <binder/IPCThreadState.h>
+#include <bionic/pac.h>
#include <hwbinder/IPCThreadState.h>
#include <utils/Log.h>
#include <cutils/memory.h>
@@ -182,6 +183,10 @@ int main(int argc, char* const argv[])
ALOGV("app_process main with argv: %s", argv_String.string());
}
+ // Because of applications that are using PAC instructions incorrectly, PAC
+ // is disabled in application processes for now.
+ ScopedDisablePAC x;
+
AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
// Process command line arguments
// ignore argv[0]
diff --git a/cmds/telecom/src/com/android/commands/telecom/Telecom.java b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
index 9c044b5e632e..52f883b5fbb7 100644
--- a/cmds/telecom/src/com/android/commands/telecom/Telecom.java
+++ b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
@@ -71,6 +71,8 @@ public final class Telecom extends BaseCommand {
private static final String COMMAND_GET_DEFAULT_DIALER = "get-default-dialer";
private static final String COMMAND_STOP_BLOCK_SUPPRESSION = "stop-block-suppression";
private static final String COMMAND_CLEANUP_STUCK_CALLS = "cleanup-stuck-calls";
+ private static final String COMMAND_CLEANUP_ORPHAN_PHONE_ACCOUNTS =
+ "cleanup-orphan-phone-accounts";
private static final String COMMAND_RESET_CAR_MODE = "reset-car-mode";
/**
@@ -125,6 +127,9 @@ public final class Telecom extends BaseCommand {
+ " provider after a call to emergency services.\n"
+ "usage: telecom cleanup-stuck-calls: Clear any disconnected calls that have"
+ " gotten wedged in Telecom.\n"
+ + "usage: telecom cleanup-orphan-phone-accounts: remove any phone accounts that"
+ + " no longer have a valid UserHandle or accounts that no longer belongs to an"
+ + " installed package.\n"
+ "usage: telecom set-emer-phone-account-filter <PACKAGE>\n"
+ "\n"
+ "telecom set-phone-account-enabled: Enables the given phone account, if it has"
@@ -227,6 +232,9 @@ public final class Telecom extends BaseCommand {
case COMMAND_CLEANUP_STUCK_CALLS:
runCleanupStuckCalls();
break;
+ case COMMAND_CLEANUP_ORPHAN_PHONE_ACCOUNTS:
+ runCleanupOrphanPhoneAccounts();
+ break;
case COMMAND_RESET_CAR_MODE:
runResetCarMode();
break;
@@ -362,6 +370,11 @@ public final class Telecom extends BaseCommand {
mTelecomService.cleanupStuckCalls();
}
+ private void runCleanupOrphanPhoneAccounts() throws RemoteException {
+ System.out.println("Success - cleaned up " + mTelecomService.cleanupOrphanPhoneAccounts()
+ + " phone accounts.");
+ }
+
private void runResetCarMode() throws RemoteException {
mTelecomService.resetCarMode();
}
diff --git a/core/api/current.txt b/core/api/current.txt
index 3234e162e5fa..86fbcee7ffad 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -125,11 +125,13 @@ package android {
field public static final String POST_NOTIFICATIONS = "android.permission.POST_NOTIFICATIONS";
field @Deprecated public static final String PROCESS_OUTGOING_CALLS = "android.permission.PROCESS_OUTGOING_CALLS";
field public static final String QUERY_ALL_PACKAGES = "android.permission.QUERY_ALL_PACKAGES";
+ field public static final String READ_ASSISTANT_APP_SEARCH_DATA = "android.permission.READ_ASSISTANT_APP_SEARCH_DATA";
field public static final String READ_BASIC_PHONE_STATE = "android.permission.READ_BASIC_PHONE_STATE";
field public static final String READ_CALENDAR = "android.permission.READ_CALENDAR";
field public static final String READ_CALL_LOG = "android.permission.READ_CALL_LOG";
field public static final String READ_CONTACTS = "android.permission.READ_CONTACTS";
field public static final String READ_EXTERNAL_STORAGE = "android.permission.READ_EXTERNAL_STORAGE";
+ field public static final String READ_HOME_APP_SEARCH_DATA = "android.permission.READ_HOME_APP_SEARCH_DATA";
field @Deprecated public static final String READ_INPUT_STATE = "android.permission.READ_INPUT_STATE";
field public static final String READ_LOGS = "android.permission.READ_LOGS";
field public static final String READ_NEARBY_STREAMING_POLICY = "android.permission.READ_NEARBY_STREAMING_POLICY";
@@ -824,6 +826,7 @@ package android {
field public static final int indicatorRight = 16843022; // 0x101010e
field public static final int indicatorStart = 16843729; // 0x10103d1
field public static final int inflatedId = 16842995; // 0x10100f3
+ field public static final int inheritKeyStoreKeys;
field public static final int inheritShowWhenLocked = 16844188; // 0x101059c
field public static final int initOrder = 16842778; // 0x101001a
field public static final int initialKeyguardLayout = 16843714; // 0x10103c2
@@ -950,6 +953,7 @@ package android {
field public static final int letterSpacing = 16843958; // 0x10104b6
field public static final int level = 16844032; // 0x1010500
field public static final int lineBreakStyle = 16844365; // 0x101064d
+ field public static final int lineBreakWordStyle = 16844366; // 0x101064e
field public static final int lineHeight = 16844159; // 0x101057f
field public static final int lineSpacingExtra = 16843287; // 0x1010217
field public static final int lineSpacingMultiplier = 16843288; // 0x1010218
@@ -1132,6 +1136,7 @@ package android {
field public static final int popupWindowStyle = 16842870; // 0x1010076
field public static final int port = 16842793; // 0x1010029
field public static final int positiveButtonText = 16843253; // 0x10101f5
+ field public static final int preferKeepClear;
field public static final int preferMinimalPostProcessing = 16844300; // 0x101060c
field public static final int preferenceCategoryStyle = 16842892; // 0x101008c
field public static final int preferenceFragmentStyle = 16844038; // 0x1010506
@@ -3995,7 +4000,7 @@ package android.app {
method @Deprecated public void onTabUnselected(android.app.ActionBar.Tab, android.app.FragmentTransaction);
}
- @UiContext public class Activity extends android.view.ContextThemeWrapper implements android.content.ComponentCallbacks2 android.view.KeyEvent.Callback android.view.LayoutInflater.Factory2 android.view.View.OnCreateContextMenuListener android.view.Window.Callback {
+ @UiContext public class Activity extends android.view.ContextThemeWrapper implements android.content.ComponentCallbacks2 android.view.KeyEvent.Callback android.view.LayoutInflater.Factory2 android.view.OnBackInvokedDispatcherOwner android.view.View.OnCreateContextMenuListener android.view.Window.Callback {
ctor public Activity();
method public void addContentView(android.view.View, android.view.ViewGroup.LayoutParams);
method public void closeContextMenu();
@@ -4038,6 +4043,7 @@ package android.app {
method public int getMaxNumPictureInPictureActions();
method public final android.media.session.MediaController getMediaController();
method @NonNull public android.view.MenuInflater getMenuInflater();
+ method @Nullable public android.view.OnBackInvokedDispatcher getOnBackInvokedDispatcher();
method public final android.app.Activity getParent();
method @Nullable public android.content.Intent getParentActivityIntent();
method public android.content.SharedPreferences getPreferences(int);
@@ -4916,7 +4922,7 @@ package android.app {
method public void onDateSet(android.widget.DatePicker, int, int, int);
}
- public class Dialog implements android.content.DialogInterface android.view.KeyEvent.Callback android.view.View.OnCreateContextMenuListener android.view.Window.Callback {
+ public class Dialog implements android.content.DialogInterface android.view.KeyEvent.Callback android.view.OnBackInvokedDispatcherOwner android.view.View.OnCreateContextMenuListener android.view.Window.Callback {
ctor public Dialog(@NonNull @UiContext android.content.Context);
ctor public Dialog(@NonNull @UiContext android.content.Context, @StyleRes int);
ctor protected Dialog(@NonNull @UiContext android.content.Context, boolean, @Nullable android.content.DialogInterface.OnCancelListener);
@@ -4936,6 +4942,7 @@ package android.app {
method @NonNull @UiContext public final android.content.Context getContext();
method @Nullable public android.view.View getCurrentFocus();
method @NonNull public android.view.LayoutInflater getLayoutInflater();
+ method @Nullable public android.view.OnBackInvokedDispatcher getOnBackInvokedDispatcher();
method @Nullable public final android.app.Activity getOwnerActivity();
method @Nullable public final android.view.SearchEvent getSearchEvent();
method public final int getVolumeControlStream();
@@ -7289,10 +7296,10 @@ package android.app.admin {
method @Nullable public java.util.List<java.lang.String> getDelegatePackages(@NonNull android.content.ComponentName, @NonNull String);
method @NonNull public java.util.List<java.lang.String> getDelegatedScopes(@Nullable android.content.ComponentName, @NonNull String);
method public CharSequence getDeviceOwnerLockScreenInfo();
- method @Nullable public android.graphics.drawable.Drawable getDrawable(int, int, @NonNull java.util.concurrent.Callable<android.graphics.drawable.Drawable>);
- method @Nullable public android.graphics.drawable.Drawable getDrawable(int, int, int, @NonNull java.util.concurrent.Callable<android.graphics.drawable.Drawable>);
- method @Nullable public android.graphics.drawable.Drawable getDrawableForDensity(int, int, int, @NonNull java.util.concurrent.Callable<android.graphics.drawable.Drawable>);
- method @Nullable public android.graphics.drawable.Drawable getDrawableForDensity(int, int, int, int, @NonNull java.util.concurrent.Callable<android.graphics.drawable.Drawable>);
+ method @NonNull public android.graphics.drawable.Drawable getDrawable(int, int, @NonNull java.util.concurrent.Callable<android.graphics.drawable.Drawable>);
+ method @NonNull public android.graphics.drawable.Drawable getDrawable(int, int, int, @NonNull java.util.concurrent.Callable<android.graphics.drawable.Drawable>);
+ method @NonNull public android.graphics.drawable.Drawable getDrawableForDensity(int, int, int, @NonNull java.util.concurrent.Callable<android.graphics.drawable.Drawable>);
+ method @NonNull public android.graphics.drawable.Drawable getDrawableForDensity(int, int, int, int, @NonNull java.util.concurrent.Callable<android.graphics.drawable.Drawable>);
method public CharSequence getEndUserSessionMessage(@NonNull android.content.ComponentName);
method @NonNull public String getEnrollmentSpecificId();
method @Nullable public android.app.admin.FactoryResetProtectionPolicy getFactoryResetProtectionPolicy(@Nullable android.content.ComponentName);
@@ -7309,6 +7316,7 @@ package android.app.admin {
method public int getMaximumFailedPasswordsForWipe(@Nullable android.content.ComponentName);
method public long getMaximumTimeToLock(@Nullable android.content.ComponentName);
method @NonNull public java.util.List<java.lang.String> getMeteredDataDisabledPackages(@NonNull android.content.ComponentName);
+ method public int getMinimumRequiredWifiSecurityLevel();
method @RequiresPermission(value=android.Manifest.permission.READ_NEARBY_STREAMING_POLICY, conditional=true) public int getNearbyAppStreamingPolicy();
method @RequiresPermission(value=android.Manifest.permission.READ_NEARBY_STREAMING_POLICY, conditional=true) public int getNearbyNotificationStreamingPolicy();
method @Deprecated @ColorInt public int getOrganizationColor(@NonNull android.content.ComponentName);
@@ -7349,6 +7357,7 @@ package android.app.admin {
method @NonNull public java.util.List<java.lang.String> getUserControlDisabledPackages(@NonNull android.content.ComponentName);
method @NonNull public android.os.Bundle getUserRestrictions(@NonNull android.content.ComponentName);
method @Nullable public String getWifiMacAddress(@NonNull android.content.ComponentName);
+ method @Nullable public android.app.admin.WifiSsidPolicy getWifiSsidPolicy();
method public boolean grantKeyPairToApp(@Nullable android.content.ComponentName, @NonNull String, @NonNull String);
method public boolean grantKeyPairToWifiAuth(@NonNull String);
method public boolean hasCaCertInstalled(@Nullable android.content.ComponentName, byte[]);
@@ -7453,6 +7462,7 @@ package android.app.admin {
method public void setMaximumFailedPasswordsForWipe(@NonNull android.content.ComponentName, int);
method public void setMaximumTimeToLock(@NonNull android.content.ComponentName, long);
method @NonNull public java.util.List<java.lang.String> setMeteredDataDisabledPackages(@NonNull android.content.ComponentName, @NonNull java.util.List<java.lang.String>);
+ method public void setMinimumRequiredWifiSecurityLevel(int);
method public void setNearbyAppStreamingPolicy(int);
method public void setNearbyNotificationStreamingPolicy(int);
method public void setNetworkLoggingEnabled(@Nullable android.content.ComponentName, boolean);
@@ -7501,6 +7511,7 @@ package android.app.admin {
method public void setUsbDataSignalingEnabled(boolean);
method public void setUserControlDisabledPackages(@NonNull android.content.ComponentName, @NonNull java.util.List<java.lang.String>);
method public void setUserIcon(@NonNull android.content.ComponentName, android.graphics.Bitmap);
+ method public void setWifiSsidPolicy(@Nullable android.app.admin.WifiSsidPolicy);
method public int startUserInBackground(@NonNull android.content.ComponentName, @NonNull android.os.UserHandle);
method public int stopUser(@NonNull android.content.ComponentName, @NonNull android.os.UserHandle);
method public boolean switchUser(@NonNull android.content.ComponentName, @Nullable android.os.UserHandle);
@@ -7552,6 +7563,7 @@ package android.app.admin {
field public static final String EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE = "android.app.extra.PROVISIONING_ACCOUNT_TO_MIGRATE";
field public static final String EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE = "android.app.extra.PROVISIONING_ADMIN_EXTRAS_BUNDLE";
field public static final String EXTRA_PROVISIONING_ALLOWED_PROVISIONING_MODES = "android.app.extra.PROVISIONING_ALLOWED_PROVISIONING_MODES";
+ field public static final String EXTRA_PROVISIONING_ALLOW_OFFLINE = "android.app.extra.PROVISIONING_ALLOW_OFFLINE";
field public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME = "android.app.extra.PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME";
field public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_MINIMUM_VERSION_CODE = "android.app.extra.PROVISIONING_DEVICE_ADMIN_MINIMUM_VERSION_CODE";
field public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM";
@@ -7595,6 +7607,7 @@ package android.app.admin {
field public static final String EXTRA_PROVISIONING_WIFI_USER_CERTIFICATE = "android.app.extra.PROVISIONING_WIFI_USER_CERTIFICATE";
field public static final String EXTRA_RESOURCE_ID = "android.app.extra.RESOURCE_ID";
field public static final String EXTRA_RESOURCE_TYPE_DRAWABLE = "android.app.extra.RESOURCE_TYPE_DRAWABLE";
+ field public static final String EXTRA_RESOURCE_TYPE_STRING = "android.app.extra.RESOURCE_TYPE_STRING";
field public static final int FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY = 1; // 0x1
field public static final int FLAG_MANAGED_CAN_ACCESS_PARENT = 2; // 0x2
field public static final int FLAG_PARENT_CAN_ACCESS_MANAGED = 1; // 0x1
@@ -7669,6 +7682,10 @@ package android.app.admin {
field public static final int RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT = 2; // 0x2
field public static final int RESET_PASSWORD_REQUIRE_ENTRY = 1; // 0x1
field public static final int SKIP_SETUP_WIZARD = 1; // 0x1
+ field public static final int WIFI_SECURITY_ENTERPRISE_192 = 3; // 0x3
+ field public static final int WIFI_SECURITY_ENTERPRISE_EAP = 2; // 0x2
+ field public static final int WIFI_SECURITY_OPEN = 0; // 0x0
+ field public static final int WIFI_SECURITY_PERSONAL = 1; // 0x1
field public static final int WIPE_EUICC = 4; // 0x4
field public static final int WIPE_EXTERNAL_STORAGE = 1; // 0x1
field public static final int WIPE_RESET_PROTECTION_DATA = 2; // 0x2
@@ -7856,6 +7873,18 @@ package android.app.admin {
field @NonNull public static final android.os.Parcelable.Creator<android.app.admin.UnsafeStateException> CREATOR;
}
+ public final class WifiSsidPolicy implements android.os.Parcelable {
+ method @NonNull public static android.app.admin.WifiSsidPolicy createAllowlistPolicy(@NonNull java.util.Set<java.lang.String>);
+ method @NonNull public static android.app.admin.WifiSsidPolicy createDenylistPolicy(@NonNull java.util.Set<java.lang.String>);
+ method public int describeContents();
+ method public int getPolicyType();
+ method @NonNull public java.util.Set<java.lang.String> getSsids();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.admin.WifiSsidPolicy> CREATOR;
+ field public static final int WIFI_SSID_POLICY_TYPE_ALLOWLIST = 0; // 0x0
+ field public static final int WIFI_SSID_POLICY_TYPE_DENYLIST = 1; // 0x1
+ }
+
}
package android.app.assist {
@@ -8848,11 +8877,12 @@ package android.bluetooth {
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public boolean isDiscovering();
method public boolean isEnabled();
method public boolean isLe2MPhySupported();
+ method public int isLeAudioBroadcastAssistantSupported();
+ method public int isLeAudioBroadcastSourceSupported();
method public int isLeAudioSupported();
method public boolean isLeCodedPhySupported();
method public boolean isLeExtendedAdvertisingSupported();
method public boolean isLePeriodicAdvertisingSupported();
- method public int isLePeriodicAdvertisingSyncTransferSenderSupported();
method public boolean isMultipleAdvertisementSupported();
method public boolean isOffloadedFilteringSupported();
method public boolean isOffloadedScanBatchingSupported();
@@ -9261,6 +9291,7 @@ package android.bluetooth {
field public static final int SOURCE_CODEC_TYPE_APTX = 2; // 0x2
field public static final int SOURCE_CODEC_TYPE_APTX_HD = 3; // 0x3
field public static final int SOURCE_CODEC_TYPE_INVALID = 1000000; // 0xf4240
+ field public static final int SOURCE_CODEC_TYPE_LC3 = 5; // 0x5
field public static final int SOURCE_CODEC_TYPE_LDAC = 4; // 0x4
field public static final int SOURCE_CODEC_TYPE_SBC = 0; // 0x0
}
@@ -9715,6 +9746,7 @@ package android.bluetooth {
method public void close();
method protected void finalize();
method @NonNull @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
+ method @Nullable @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public android.bluetooth.BluetoothDevice getConnectedGroupLeadDevice(int);
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);
@@ -9753,6 +9785,7 @@ package android.bluetooth {
field public static final String EXTRA_STATE = "android.bluetooth.profile.extra.STATE";
field public static final int GATT = 7; // 0x7
field public static final int GATT_SERVER = 8; // 0x8
+ field public static final int HAP_CLIENT = 28; // 0x1c
field public static final int HEADSET = 1; // 0x1
field @Deprecated public static final int HEALTH = 3; // 0x3
field public static final int HEARING_AID = 21; // 0x15
@@ -10928,10 +10961,10 @@ package android.content {
method @Nullable public abstract android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter, @Nullable String, @Nullable android.os.Handler, int);
method @Deprecated @RequiresPermission(android.Manifest.permission.BROADCAST_STICKY) public abstract void removeStickyBroadcast(@RequiresPermission android.content.Intent);
method @Deprecated @RequiresPermission(allOf={"android.permission.INTERACT_ACROSS_USERS", android.Manifest.permission.BROADCAST_STICKY}) public abstract void removeStickyBroadcastAsUser(@RequiresPermission android.content.Intent, android.os.UserHandle);
+ method public void revokeOwnPermissionOnKill(@NonNull String);
+ method public void revokeOwnPermissionsOnKill(@NonNull java.util.Collection<java.lang.String>);
method public abstract void revokeUriPermission(android.net.Uri, int);
method public abstract void revokeUriPermission(String, android.net.Uri, int);
- method public void selfRevokePermission(@NonNull String);
- method public void selfRevokePermissions(@NonNull java.util.Collection<java.lang.String>);
method public abstract void sendBroadcast(@RequiresPermission android.content.Intent);
method public abstract void sendBroadcast(@RequiresPermission android.content.Intent, @Nullable String);
method @RequiresPermission("android.permission.INTERACT_ACROSS_USERS") public abstract void sendBroadcastAsUser(@RequiresPermission android.content.Intent, android.os.UserHandle);
@@ -11059,8 +11092,8 @@ package android.content {
field public static final String TELEPHONY_SUBSCRIPTION_SERVICE = "telephony_subscription_service";
field public static final String TEXT_CLASSIFICATION_SERVICE = "textclassification";
field public static final String TEXT_SERVICES_MANAGER_SERVICE = "textservices";
- field public static final String TV_IAPP_SERVICE = "tv_iapp";
field public static final String TV_INPUT_SERVICE = "tv_input";
+ field public static final String TV_INTERACTIVE_APP_SERVICE = "tv_interactive_app";
field public static final String UI_MODE_SERVICE = "uimode";
field public static final String USAGE_STATS_SERVICE = "usagestats";
field public static final String USB_SERVICE = "usb";
@@ -13179,6 +13212,7 @@ package android.content.pm {
field public static final String FEATURE_WIFI_DIRECT = "android.hardware.wifi.direct";
field public static final String FEATURE_WIFI_PASSPOINT = "android.hardware.wifi.passpoint";
field public static final String FEATURE_WIFI_RTT = "android.hardware.wifi.rtt";
+ field public static final String FEATURE_WINDOW_MAGNIFICATION = "android.software.window_magnification";
field public static final int FLAG_PERMISSION_WHITELIST_INSTALLER = 2; // 0x2
field public static final int FLAG_PERMISSION_WHITELIST_SYSTEM = 1; // 0x1
field public static final int FLAG_PERMISSION_WHITELIST_UPGRADE = 4; // 0x4
@@ -13471,6 +13505,7 @@ package android.content.pm {
public final class ShortcutInfo implements android.os.Parcelable {
method public int describeContents();
method @Nullable public android.content.ComponentName getActivity();
+ method @NonNull public java.util.List<java.lang.String> getCapabilityParameterValues(@NonNull String, @NonNull String);
method @Nullable public java.util.Set<java.lang.String> getCategories();
method @Nullable public CharSequence getDisabledMessage();
method public int getDisabledReason();
@@ -13485,6 +13520,7 @@ package android.content.pm {
method public int getRank();
method @Nullable public CharSequence getShortLabel();
method public android.os.UserHandle getUserHandle();
+ method public boolean hasCapability(@NonNull String);
method public boolean hasKeyFieldsOnly();
method public boolean isCached();
method public boolean isDeclaredInManifest();
@@ -13509,6 +13545,7 @@ package android.content.pm {
public static class ShortcutInfo.Builder {
ctor public ShortcutInfo.Builder(android.content.Context, String);
+ method @NonNull public android.content.pm.ShortcutInfo.Builder addCapabilityBinding(@NonNull String, @Nullable String, @Nullable java.util.List<java.lang.String>);
method @NonNull public android.content.pm.ShortcutInfo build();
method @NonNull public android.content.pm.ShortcutInfo.Builder setActivity(@NonNull android.content.ComponentName);
method @NonNull public android.content.pm.ShortcutInfo.Builder setCategories(java.util.Set<java.lang.String>);
@@ -17582,12 +17619,16 @@ package android.graphics.text {
public final class LineBreakConfig {
ctor public LineBreakConfig();
method public int getLineBreakStyle();
- method public void set(@Nullable android.graphics.text.LineBreakConfig);
+ method public int getLineBreakWordStyle();
+ method public void set(@NonNull android.graphics.text.LineBreakConfig);
method public void setLineBreakStyle(int);
+ method public void setLineBreakWordStyle(int);
field public static final int LINE_BREAK_STYLE_LOOSE = 1; // 0x1
field public static final int LINE_BREAK_STYLE_NONE = 0; // 0x0
field public static final int LINE_BREAK_STYLE_NORMAL = 2; // 0x2
field public static final int LINE_BREAK_STYLE_STRICT = 3; // 0x3
+ field public static final int LINE_BREAK_WORD_STYLE_NONE = 0; // 0x0
+ field public static final int LINE_BREAK_WORD_STYLE_PHRASE = 1; // 0x1
}
public class LineBreaker {
@@ -18043,6 +18084,7 @@ package android.hardware {
field public static final long USAGE_CPU_READ_RARELY = 2L; // 0x2L
field public static final long USAGE_CPU_WRITE_OFTEN = 48L; // 0x30L
field public static final long USAGE_CPU_WRITE_RARELY = 32L; // 0x20L
+ field public static final long USAGE_FRONT_BUFFER = 1L; // 0x1L
field public static final long USAGE_GPU_COLOR_OUTPUT = 512L; // 0x200L
field public static final long USAGE_GPU_CUBE_MAP = 33554432L; // 0x2000000L
field public static final long USAGE_GPU_DATA_BUFFER = 16777216L; // 0x1000000L
@@ -18536,12 +18578,14 @@ package android.hardware.camera2 {
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> REPROCESS_MAX_CAPTURE_STALL;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> REQUEST_AVAILABLE_CAPABILITIES;
+ field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.DynamicRangeProfiles> REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> REQUEST_MAX_NUM_INPUT_STREAMS;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> REQUEST_MAX_NUM_OUTPUT_PROC;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> REQUEST_MAX_NUM_OUTPUT_PROC_STALLING;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> REQUEST_MAX_NUM_OUTPUT_RAW;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> REQUEST_PARTIAL_RESULT_COUNT;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Byte> REQUEST_PIPELINE_MAX_DEPTH;
+ field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> REQUEST_RECOMMENDED_TEN_BIT_DYNAMIC_RANGE_PROFILE;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Float> SCALER_AVAILABLE_MAX_DIGITAL_ZOOM;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> SCALER_AVAILABLE_ROTATE_AND_CROP_MODES;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> SCALER_CROPPING_TYPE;
@@ -18549,6 +18593,7 @@ package android.hardware.camera2 {
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_CONCURRENT_STREAM_COMBINATIONS;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_MAXIMUM_RESOLUTION_STREAM_COMBINATIONS;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_STREAM_COMBINATIONS;
+ field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_TEN_BIT_OUTPUT_STREAM_COMBINATIONS;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MultiResolutionStreamConfigurationMap> SCALER_MULTI_RESOLUTION_STREAM_CONFIGURATION_MAP;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.StreamConfigurationMap> SCALER_STREAM_CONFIGURATION_MAP;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.StreamConfigurationMap> SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION;
@@ -18865,6 +18910,7 @@ package android.hardware.camera2 {
field public static final int REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE = 6; // 0x6
field public static final int REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO = 9; // 0x9
field public static final int REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT = 8; // 0x8
+ field public static final int REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT = 18; // 0x12
field public static final int REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA = 11; // 0xb
field public static final int REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING = 2; // 0x2
field public static final int REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR = 1; // 0x1
@@ -19218,6 +19264,25 @@ package android.hardware.camera2.params {
field public static final long NORMAL = 0L; // 0x0L
}
+ public final class DynamicRangeProfiles {
+ ctor public DynamicRangeProfiles(@NonNull int[]);
+ method @NonNull public java.util.Set<java.lang.Integer> getProfileCaptureRequestConstraints(int);
+ method @NonNull public java.util.Set<java.lang.Integer> getSupportedProfiles();
+ field public static final int DOLBY_VISION_10B_HDR_OEM = 64; // 0x40
+ field public static final int DOLBY_VISION_10B_HDR_OEM_PO = 128; // 0x80
+ field public static final int DOLBY_VISION_10B_HDR_REF = 16; // 0x10
+ field public static final int DOLBY_VISION_10B_HDR_REF_PO = 32; // 0x20
+ field public static final int DOLBY_VISION_8B_HDR_OEM = 1024; // 0x400
+ field public static final int DOLBY_VISION_8B_HDR_OEM_PO = 2048; // 0x800
+ field public static final int DOLBY_VISION_8B_HDR_REF = 256; // 0x100
+ field public static final int DOLBY_VISION_8B_HDR_REF_PO = 512; // 0x200
+ field public static final int HDR10 = 4; // 0x4
+ field public static final int HDR10_PLUS = 8; // 0x8
+ field public static final int HLG10 = 2; // 0x2
+ field public static final int PUBLIC_MAX = 4096; // 0x1000
+ field public static final int STANDARD = 1; // 0x1
+ }
+
public final class ExtensionSessionConfiguration {
ctor public ExtensionSessionConfiguration(int, @NonNull java.util.List<android.hardware.camera2.params.OutputConfiguration>, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.camera2.CameraExtensionSession.StateCallback);
method @NonNull public java.util.concurrent.Executor getExecutor();
@@ -19264,8 +19329,10 @@ package android.hardware.camera2.params {
}
public static final class MandatoryStreamCombination.MandatoryStreamInformation {
+ method public int get10BitFormat();
method @NonNull public java.util.List<android.util.Size> getAvailableSizes();
method public int getFormat();
+ method public boolean is10BitCapable();
method public boolean isInput();
method public boolean isMaximumSize();
method public boolean isUltraHighResolution();
@@ -19319,12 +19386,14 @@ package android.hardware.camera2.params {
method @NonNull public static java.util.Collection<android.hardware.camera2.params.OutputConfiguration> createInstancesForMultiResolutionOutput(@NonNull android.hardware.camera2.MultiResolutionImageReader);
method public int describeContents();
method public void enableSurfaceSharing();
+ method public int getDynamicRangeProfile();
method public int getMaxSharedSurfaceCount();
method @Nullable public android.view.Surface getSurface();
method public int getSurfaceGroupId();
method @NonNull public java.util.List<android.view.Surface> getSurfaces();
method public void removeSensorPixelModeUsed(int);
method public void removeSurface(@NonNull android.view.Surface);
+ method public void setDynamicRangeProfile(int);
method public void setPhysicalCameraId(@Nullable String);
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.hardware.camera2.params.OutputConfiguration> CREATOR;
@@ -19350,6 +19419,7 @@ package android.hardware.camera2.params {
method @Nullable public java.util.Set<java.lang.Integer> getValidOutputFormatsForInput(int);
method public boolean isOutputSupportedFor(int);
method public boolean isOutputSupportedFor(@NonNull android.view.Surface);
+ field public static final int USECASE_10BIT_OUTPUT = 8; // 0x8
field public static final int USECASE_LOW_LATENCY_SNAPSHOT = 6; // 0x6
field public static final int USECASE_PREVIEW = 0; // 0x0
field public static final int USECASE_RAW = 5; // 0x5
@@ -20891,6 +20961,7 @@ package android.media {
method public boolean isSink();
method public boolean isSource();
field public static final int TYPE_AUX_LINE = 19; // 0x13
+ field public static final int TYPE_BLE_BROADCAST = 30; // 0x1e
field public static final int TYPE_BLE_HEADSET = 26; // 0x1a
field public static final int TYPE_BLE_SPEAKER = 27; // 0x1b
field public static final int TYPE_BLUETOOTH_A2DP = 8; // 0x8
@@ -22041,14 +22112,30 @@ package android.media {
public class ImageWriter implements java.lang.AutoCloseable {
method public void close();
method public android.media.Image dequeueInputImage();
+ method public long getDataSpace();
method public int getFormat();
+ method public int getHardwareBufferFormat();
+ method public int getHeight();
method public int getMaxImages();
+ method public long getUsage();
+ method public int getWidth();
method @NonNull public static android.media.ImageWriter newInstance(@NonNull android.view.Surface, @IntRange(from=1) int);
method @NonNull public static android.media.ImageWriter newInstance(@NonNull android.view.Surface, @IntRange(from=1) int, int);
method public void queueInputImage(android.media.Image);
method public void setOnImageReleasedListener(android.media.ImageWriter.OnImageReleasedListener, android.os.Handler);
}
+ public static final class ImageWriter.Builder {
+ ctor public ImageWriter.Builder(@NonNull android.view.Surface);
+ method @NonNull public android.media.ImageWriter build();
+ method @NonNull public android.media.ImageWriter.Builder setDataSpace(long);
+ method @NonNull public android.media.ImageWriter.Builder setHardwareBufferFormat(int);
+ method @NonNull public android.media.ImageWriter.Builder setImageFormat(int);
+ method @NonNull public android.media.ImageWriter.Builder setMaxImages(@IntRange(from=1) int);
+ method @NonNull public android.media.ImageWriter.Builder setUsage(long);
+ method @NonNull public android.media.ImageWriter.Builder setWidthAndHeight(@IntRange(from=1) int, @IntRange(from=1) int);
+ }
+
public static interface ImageWriter.OnImageReleasedListener {
method public void onImageReleased(android.media.ImageWriter);
}
@@ -22460,6 +22547,7 @@ package android.media {
field @Deprecated public static final int COLOR_TI_FormatYUV420PackedSemiPlanar = 2130706688; // 0x7f000100
field public static final String FEATURE_AdaptivePlayback = "adaptive-playback";
field public static final String FEATURE_DynamicTimestamp = "dynamic-timestamp";
+ field public static final String FEATURE_EncodingStatistics = "encoding-statistics";
field public static final String FEATURE_FrameParsing = "frame-parsing";
field public static final String FEATURE_IntraRefresh = "intra-refresh";
field public static final String FEATURE_LowLatency = "low-latency";
@@ -23234,6 +23322,7 @@ package android.media {
field public static final String KEY_OPERATING_RATE = "operating-rate";
field public static final String KEY_OUTPUT_REORDER_DEPTH = "output-reorder-depth";
field public static final String KEY_PCM_ENCODING = "pcm-encoding";
+ field public static final String KEY_PICTURE_TYPE = "picture-type";
field public static final String KEY_PIXEL_ASPECT_RATIO_HEIGHT = "sar-height";
field public static final String KEY_PIXEL_ASPECT_RATIO_WIDTH = "sar-width";
field public static final String KEY_PREPEND_HEADER_TO_SYNC_FRAMES = "prepend-sps-pps-to-idr-frames";
@@ -23251,6 +23340,8 @@ package android.media {
field public static final String KEY_TILE_HEIGHT = "tile-height";
field public static final String KEY_TILE_WIDTH = "tile-width";
field public static final String KEY_TRACK_ID = "track-id";
+ field public static final String KEY_VIDEO_ENCODING_STATISTICS_LEVEL = "video-encoding-statistics-level";
+ field public static final String KEY_VIDEO_QP_AVERAGE = "video-qp-average";
field public static final String KEY_VIDEO_QP_B_MAX = "video-qp-b-max";
field public static final String KEY_VIDEO_QP_B_MIN = "video-qp-b-min";
field public static final String KEY_VIDEO_QP_I_MAX = "video-qp-i-max";
@@ -23311,12 +23402,18 @@ package android.media {
field public static final String MIMETYPE_VIDEO_SCRAMBLED = "video/scrambled";
field public static final String MIMETYPE_VIDEO_VP8 = "video/x-vnd.on2.vp8";
field public static final String MIMETYPE_VIDEO_VP9 = "video/x-vnd.on2.vp9";
+ field public static final int PICTURE_TYPE_B = 3; // 0x3
+ field public static final int PICTURE_TYPE_I = 1; // 0x1
+ field public static final int PICTURE_TYPE_P = 2; // 0x2
+ field public static final int PICTURE_TYPE_UNKNOWN = 0; // 0x0
field public static final int TYPE_BYTE_BUFFER = 5; // 0x5
field public static final int TYPE_FLOAT = 3; // 0x3
field public static final int TYPE_INTEGER = 1; // 0x1
field public static final int TYPE_LONG = 2; // 0x2
field public static final int TYPE_NULL = 0; // 0x0
field public static final int TYPE_STRING = 4; // 0x4
+ field public static final int VIDEO_ENCODING_STATISTICS_LEVEL_1 = 1; // 0x1
+ field public static final int VIDEO_ENCODING_STATISTICS_LEVEL_NONE = 0; // 0x0
}
public final class MediaMetadata implements android.os.Parcelable {
@@ -25679,6 +25776,7 @@ package android.media.midi {
public final class MidiDeviceInfo implements android.os.Parcelable {
method public int describeContents();
+ method public int getDefaultProtocol();
method public int getId();
method public int getInputPortCount();
method public int getOutputPortCount();
@@ -25695,6 +25793,14 @@ package android.media.midi {
field public static final String PROPERTY_SERIAL_NUMBER = "serial_number";
field public static final String PROPERTY_USB_DEVICE = "usb_device";
field public static final String PROPERTY_VERSION = "version";
+ field public static final int PROTOCOL_UMP_MIDI_1_0_UP_TO_128_BITS = 3; // 0x3
+ field public static final int PROTOCOL_UMP_MIDI_1_0_UP_TO_128_BITS_AND_JRTS = 4; // 0x4
+ field public static final int PROTOCOL_UMP_MIDI_1_0_UP_TO_64_BITS = 1; // 0x1
+ field public static final int PROTOCOL_UMP_MIDI_1_0_UP_TO_64_BITS_AND_JRTS = 2; // 0x2
+ field public static final int PROTOCOL_UMP_MIDI_2_0 = 17; // 0x11
+ field public static final int PROTOCOL_UMP_MIDI_2_0_AND_JRTS = 18; // 0x12
+ field public static final int PROTOCOL_UMP_USE_MIDI_CI = 0; // 0x0
+ field public static final int PROTOCOL_UNKNOWN = -1; // 0xffffffff
field public static final int TYPE_BLUETOOTH = 3; // 0x3
field public static final int TYPE_USB = 1; // 0x1
field public static final int TYPE_VIRTUAL = 2; // 0x2
@@ -25735,11 +25841,15 @@ package android.media.midi {
}
public final class MidiManager {
- method public android.media.midi.MidiDeviceInfo[] getDevices();
+ method @Deprecated public android.media.midi.MidiDeviceInfo[] getDevices();
+ method @NonNull public java.util.Set<android.media.midi.MidiDeviceInfo> getDevicesForTransport(int);
method public void openBluetoothDevice(android.bluetooth.BluetoothDevice, android.media.midi.MidiManager.OnDeviceOpenedListener, android.os.Handler);
method public void openDevice(android.media.midi.MidiDeviceInfo, android.media.midi.MidiManager.OnDeviceOpenedListener, android.os.Handler);
- method public void registerDeviceCallback(android.media.midi.MidiManager.DeviceCallback, android.os.Handler);
+ method @Deprecated public void registerDeviceCallback(android.media.midi.MidiManager.DeviceCallback, android.os.Handler);
+ method public void registerDeviceCallback(int, @NonNull java.util.concurrent.Executor, @NonNull android.media.midi.MidiManager.DeviceCallback);
method public void unregisterDeviceCallback(android.media.midi.MidiManager.DeviceCallback);
+ field public static final int TRANSPORT_MIDI_BYTE_STREAM = 1; // 0x1
+ field public static final int TRANSPORT_UNIVERSAL_MIDI_PACKETS = 2; // 0x2
}
public static class MidiManager.DeviceCallback {
@@ -26800,16 +26910,43 @@ package android.media.tv {
package android.media.tv.interactive {
- public final class TvIAppManager {
+ public final class TvInteractiveAppInfo implements android.os.Parcelable {
+ ctor public TvInteractiveAppInfo(@NonNull android.content.Context, @NonNull android.content.ComponentName);
+ method public int describeContents();
+ method @NonNull public String getId();
+ method @Nullable public android.content.pm.ServiceInfo getServiceInfo();
+ method @NonNull public int getSupportedTypes();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.media.tv.interactive.TvInteractiveAppInfo> CREATOR;
+ field public static final int INTERACTIVE_APP_TYPE_ATSC = 2; // 0x2
+ field public static final int INTERACTIVE_APP_TYPE_GINGA = 4; // 0x4
+ field public static final int INTERACTIVE_APP_TYPE_HBBTV = 1; // 0x1
+ }
+
+ public final class TvInteractiveAppManager {
+ method @NonNull public java.util.List<android.media.tv.interactive.TvInteractiveAppInfo> getTvInteractiveAppServiceList();
}
- public abstract class TvIAppService extends android.app.Service {
- ctor public TvIAppService();
+ public abstract class TvInteractiveAppService extends android.app.Service {
+ ctor public TvInteractiveAppService();
method public final android.os.IBinder onBind(android.content.Intent);
- field public static final String SERVICE_INTERFACE = "android.media.tv.interactive.TvIAppService";
+ field public static final String SERVICE_INTERFACE = "android.media.tv.interactive.TvInteractiveAppService";
field public static final String SERVICE_META_DATA = "android.media.tv.interactive.app";
}
+ public class TvInteractiveAppView extends android.view.ViewGroup {
+ ctor public TvInteractiveAppView(@NonNull android.content.Context);
+ ctor public TvInteractiveAppView(@NonNull android.content.Context, @Nullable android.util.AttributeSet);
+ ctor public TvInteractiveAppView(@NonNull android.content.Context, @Nullable android.util.AttributeSet, int);
+ method public void clearCallback();
+ method public void setCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.tv.interactive.TvInteractiveAppView.TvInteractiveAppCallback);
+ method public void startInteractiveApp();
+ }
+
+ public abstract static class TvInteractiveAppView.TvInteractiveAppCallback {
+ ctor public TvInteractiveAppView.TvInteractiveAppCallback();
+ }
+
}
package android.mtp {
@@ -32009,6 +32146,7 @@ package android.os {
method @IntRange(from=0xffffffff) public int indexOf(java.util.Locale);
method public boolean isEmpty();
method public static boolean isPseudoLocale(@Nullable android.icu.util.ULocale);
+ method public static boolean matchesLanguageAndScript(@NonNull java.util.Locale, @NonNull java.util.Locale);
method public static void setDefault(@NonNull @Size(min=1) android.os.LocaleList);
method @IntRange(from=0) public int size();
method @NonNull public String toLanguageTags();
@@ -32186,7 +32324,7 @@ package android.os {
method @Deprecated public void readMap(@NonNull java.util.Map, @Nullable ClassLoader);
method public <K, V> void readMap(@NonNull java.util.Map<? super K,? super V>, @Nullable ClassLoader, @NonNull Class<K>, @NonNull Class<V>);
method @Deprecated @Nullable public <T extends android.os.Parcelable> T readParcelable(@Nullable ClassLoader);
- method @Nullable public <T> T readParcelable(@Nullable ClassLoader, @NonNull Class<T>);
+ method @Nullable public <T extends android.os.Parcelable> T readParcelable(@Nullable ClassLoader, @NonNull Class<? super T>);
method @Deprecated @Nullable public android.os.Parcelable[] readParcelableArray(@Nullable ClassLoader);
method @Nullable public <T> T[] readParcelableArray(@Nullable ClassLoader, @NonNull Class<T>);
method @Deprecated @Nullable public android.os.Parcelable.Creator<?> readParcelableCreator(@Nullable ClassLoader);
@@ -32196,7 +32334,7 @@ package android.os {
method @Nullable public android.os.PersistableBundle readPersistableBundle();
method @Nullable public android.os.PersistableBundle readPersistableBundle(@Nullable ClassLoader);
method @Deprecated @Nullable public java.io.Serializable readSerializable();
- method @Nullable public <T> T readSerializable(@Nullable ClassLoader, @NonNull Class<T>);
+ method @Nullable public <T extends java.io.Serializable> T readSerializable(@Nullable ClassLoader, @NonNull Class<? super T>);
method @NonNull public android.util.Size readSize();
method @NonNull public android.util.SizeF readSizeF();
method @Deprecated @Nullable public <T> android.util.SparseArray<T> readSparseArray(@Nullable ClassLoader);
@@ -32466,6 +32604,7 @@ package android.os {
method public static final boolean is64Bit();
method public static boolean isApplicationUid(int);
method public static final boolean isIsolated();
+ method public static final boolean isSupplemental();
method public static final void killProcess(int);
method public static final int myPid();
method @NonNull public static String myProcessName();
@@ -32906,9 +33045,13 @@ package android.os {
method @NonNull public int[] areEffectsSupported(@NonNull int...);
method @NonNull public boolean[] arePrimitivesSupported(@NonNull int...);
method @RequiresPermission(android.Manifest.permission.VIBRATE) public abstract void cancel();
+ method @Nullable public android.os.vibrator.VibratorFrequencyProfile getFrequencyProfile();
method public int getId();
method @NonNull public int[] getPrimitiveDurations(@NonNull int...);
+ method public float getQFactor();
+ method public float getResonantFrequency();
method public abstract boolean hasAmplitudeControl();
+ method public boolean hasFrequencyControl();
method public abstract boolean hasVibrator();
method @Deprecated @RequiresPermission(android.Manifest.permission.VIBRATE) public void vibrate(long);
method @Deprecated @RequiresPermission(android.Manifest.permission.VIBRATE) public void vibrate(long, android.media.AudioAttributes);
@@ -33234,6 +33377,17 @@ package android.os.strictmode {
}
+package android.os.vibrator {
+
+ public final class VibratorFrequencyProfile {
+ method public float getMaxAmplitudeMeasurementInterval();
+ method @FloatRange(from=0, to=1) @NonNull public float[] getMaxAmplitudeMeasurements();
+ method public float getMaxFrequency();
+ method public float getMinFrequency();
+ }
+
+}
+
package android.preference {
@Deprecated public class CheckBoxPreference extends android.preference.TwoStatePreference {
@@ -36231,7 +36385,7 @@ package android.provider {
field public static final String DTMF_TONE_WHEN_DIALING = "dtmf_tone";
field public static final String END_BUTTON_BEHAVIOR = "end_button_behavior";
field public static final String FONT_SCALE = "font_scale";
- field public static final String HAPTIC_FEEDBACK_ENABLED = "haptic_feedback_enabled";
+ field @Deprecated public static final String HAPTIC_FEEDBACK_ENABLED = "haptic_feedback_enabled";
field @Deprecated public static final String HTTP_PROXY = "http_proxy";
field @Deprecated public static final String INSTALL_NON_MARKET_APPS = "install_non_market_apps";
field @Deprecated public static final String LOCATION_PROVIDERS_ALLOWED = "location_providers_allowed";
@@ -36275,7 +36429,7 @@ package android.provider {
field public static final String USER_ROTATION = "user_rotation";
field @Deprecated public static final String USE_GOOGLE_MAIL = "use_google_mail";
field public static final String VIBRATE_ON = "vibrate_on";
- field public static final String VIBRATE_WHEN_RINGING = "vibrate_when_ringing";
+ field @Deprecated public static final String VIBRATE_WHEN_RINGING = "vibrate_when_ringing";
field @Deprecated public static final String WAIT_FOR_DEBUGGER = "wait_for_debugger";
field @Deprecated public static final String WALLPAPER_ACTIVITY = "wallpaper_activity";
field @Deprecated public static final String WIFI_MAX_DHCP_RETRY_COUNT = "wifi_max_dhcp_retry_count";
@@ -40384,6 +40538,7 @@ package android.telecom {
public final class Call {
method public void addConferenceParticipants(@NonNull java.util.List<android.net.Uri>);
method public void answer(int);
+ method public void answerCall(@NonNull android.telecom.CallEndpoint, int);
method public void conference(android.telecom.Call);
method public void deflect(android.net.Uri);
method public void disconnect();
@@ -40404,7 +40559,9 @@ package android.telecom {
method public void phoneAccountSelected(android.telecom.PhoneAccountHandle, boolean);
method public void playDtmfTone(char);
method public void postDialContinue(boolean);
- method public void pullExternalCall();
+ method public void pullCall();
+ method @Deprecated public void pullExternalCall();
+ method public void pushCall(@NonNull android.telecom.CallEndpoint);
method public void putExtras(android.os.Bundle);
method public void registerCallback(android.telecom.Call.Callback);
method public void registerCallback(android.telecom.Call.Callback, android.os.Handler);
@@ -40447,7 +40604,10 @@ package android.telecom {
public abstract static class Call.Callback {
ctor public Call.Callback();
+ method public void onAnswerFailed(@NonNull android.telecom.CallEndpoint, int);
method public void onCallDestroyed(android.telecom.Call);
+ method public void onCallPullFailed(int);
+ method public void onCallPushFailed(@NonNull android.telecom.CallEndpoint, int);
method public void onCannedTextResponsesLoaded(android.telecom.Call, java.util.List<java.lang.String>);
method public void onChildrenChanged(android.telecom.Call, java.util.List<android.telecom.Call>);
method public void onConferenceableCallsChanged(android.telecom.Call, java.util.List<android.telecom.Call>);
@@ -40463,11 +40623,22 @@ package android.telecom {
method public void onRttStatusChanged(android.telecom.Call, boolean, android.telecom.Call.RttCall);
method public void onStateChanged(android.telecom.Call, int);
method public void onVideoCallChanged(android.telecom.Call, android.telecom.InCallService.VideoCall);
+ field public static final int ANSWER_FAILED_ENDPOINT_REJECTED = 3; // 0x3
+ field public static final int ANSWER_FAILED_ENDPOINT_TIMEOUT = 2; // 0x2
+ field public static final int ANSWER_FAILED_ENDPOINT_UNAVAILABLE = 1; // 0x1
+ field public static final int ANSWER_FAILED_UNKNOWN_REASON = 0; // 0x0
field public static final int HANDOVER_FAILURE_DEST_APP_REJECTED = 1; // 0x1
field public static final int HANDOVER_FAILURE_NOT_SUPPORTED = 2; // 0x2
field public static final int HANDOVER_FAILURE_ONGOING_EMERGENCY_CALL = 4; // 0x4
field public static final int HANDOVER_FAILURE_UNKNOWN = 5; // 0x5
field public static final int HANDOVER_FAILURE_USER_REJECTED = 3; // 0x3
+ field public static final int PULL_FAILED_ENDPOINT_REJECTED = 2; // 0x2
+ field public static final int PULL_FAILED_ENDPOINT_TIMEOUT = 1; // 0x1
+ field public static final int PULL_FAILED_UNKNOWN_REASON = 0; // 0x0
+ field public static final int PUSH_FAILED_ENDPOINT_REJECTED = 3; // 0x3
+ field public static final int PUSH_FAILED_ENDPOINT_TIMEOUT = 2; // 0x2
+ field public static final int PUSH_FAILED_ENDPOINT_UNAVAILABLE = 1; // 0x1
+ field public static final int PUSH_FAILED_UNKNOWN_REASON = 0; // 0x0
}
public static class Call.Details {
@@ -40475,6 +40646,8 @@ package android.telecom {
method public boolean can(int);
method public static String capabilitiesToString(int);
method public android.telecom.PhoneAccountHandle getAccountHandle();
+ method @Nullable public android.telecom.CallEndpoint getActiveCallEndpoint();
+ method @NonNull public java.util.Set<android.telecom.CallEndpoint> getAvailableCallEndpoints();
method public int getCallCapabilities();
method public int getCallDirection();
method public int getCallProperties();
@@ -40568,6 +40741,34 @@ package android.telecom {
field public static final int ROUTE_WIRED_OR_EARPIECE = 5; // 0x5
}
+ public final class CallEndpoint implements android.os.Parcelable {
+ ctor public CallEndpoint(@NonNull android.os.ParcelUuid, @NonNull CharSequence, int, @NonNull android.content.ComponentName);
+ method public int describeContents();
+ method @NonNull public CharSequence getDescription();
+ method @NonNull public android.os.ParcelUuid getIdentifier();
+ method public int getType();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telecom.CallEndpoint> CREATOR;
+ field public static final int ENDPOINT_TYPE_TETHERED = 2; // 0x2
+ field public static final int ENDPOINT_TYPE_UNTETHERED = 1; // 0x1
+ }
+
+ public interface CallEndpointCallback {
+ method public void onCallEndpointSessionActivationTimeout();
+ method public void onCallEndpointSessionDeactivated();
+ }
+
+ public class CallEndpointSession {
+ method public void setCallEndpointSessionActivated();
+ method public void setCallEndpointSessionActivationFailed(int);
+ method public void setCallEndpointSessionDeactivated();
+ field public static final int ACTIVATION_FAILURE_REJECTED = 1; // 0x1
+ field public static final int ACTIVATION_FAILURE_UNAVAILABLE = 0; // 0x0
+ field public static final int ANSWER_REQUEST = 1; // 0x1
+ field public static final int PLACE_REQUEST = 3; // 0x3
+ field public static final int PUSH_REQUEST = 2; // 0x2
+ }
+
public abstract class CallRedirectionService extends android.app.Service {
ctor public CallRedirectionService();
method public final void cancelCall();
@@ -40837,7 +41038,6 @@ package android.telecom {
field public static final int PROPERTY_IS_RTT = 256; // 0x100
field public static final int PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL = 1024; // 0x400
field public static final int PROPERTY_SELF_MANAGED = 128; // 0x80
- field public static final int PROPERTY_TETHERED_CALL = 16384; // 0x4000
field public static final int PROPERTY_WIFI = 8; // 0x8
field public static final int STATE_ACTIVE = 4; // 0x4
field public static final int STATE_DIALING = 3; // 0x3
@@ -40971,6 +41171,8 @@ package android.telecom {
field public static final int OTHER = 9; // 0x9
field public static final String REASON_EMERGENCY_CALL_PLACED = "REASON_EMERGENCY_CALL_PLACED";
field public static final String REASON_EMULATING_SINGLE_CALL = "EMULATING_SINGLE_CALL";
+ field public static final String REASON_ENDPOINT_REJECTED = "REASON_ENDPOINT_REJECTED";
+ field public static final String REASON_ENDPOINT_SESSION_DEACTIVATED = "REASON_ENDPOINT_SESSION_DEACTIVATED";
field public static final String REASON_IMS_ACCESS_BLOCKED = "REASON_IMS_ACCESS_BLOCKED";
field public static final String REASON_WIFI_ON_BUT_WFC_OFF = "REASON_WIFI_ON_BUT_WFC_OFF";
field public static final int REJECTED = 6; // 0x6
@@ -40999,6 +41201,7 @@ package android.telecom {
method public void onBringToForeground(boolean);
method public void onCallAdded(android.telecom.Call);
method public void onCallAudioStateChanged(android.telecom.CallAudioState);
+ method @NonNull public android.telecom.CallEndpointCallback onCallEndpointActivationRequested(@NonNull android.telecom.CallEndpoint, @NonNull android.telecom.CallEndpointSession) throws java.lang.UnsupportedOperationException;
method public void onCallRemoved(android.telecom.Call);
method public void onCanAddCallChanged(boolean);
method public void onConnectionEvent(android.telecom.Call, String, android.os.Bundle);
@@ -41261,11 +41464,12 @@ package android.telecom {
method @Deprecated @RequiresPermission(android.Manifest.permission.ANSWER_PHONE_CALLS) public boolean endCall();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.net.Uri getAdnUriForPhoneAccount(android.telecom.PhoneAccountHandle);
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public java.util.List<android.telecom.PhoneAccountHandle> getCallCapablePhoneAccounts();
+ method @NonNull public java.util.Set<android.telecom.CallEndpoint> getCallEndpoints();
method public String getDefaultDialerPackage();
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public android.telecom.PhoneAccountHandle getDefaultOutgoingPhoneAccount(String);
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.READ_SMS, android.Manifest.permission.READ_PHONE_NUMBERS}, conditional=true) public String getLine1Number(android.telecom.PhoneAccountHandle);
method public android.telecom.PhoneAccount getPhoneAccount(android.telecom.PhoneAccountHandle);
- method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public java.util.List<android.telecom.PhoneAccountHandle> getSelfManagedPhoneAccounts();
+ method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.MANAGE_OWN_CALLS}) public java.util.List<android.telecom.PhoneAccountHandle> getSelfManagedPhoneAccounts();
method public android.telecom.PhoneAccountHandle getSimCallManager();
method @Nullable public android.telecom.PhoneAccountHandle getSimCallManagerForSubscription(int);
method @Nullable public String getSystemDialerPackage();
@@ -41281,10 +41485,12 @@ package android.telecom {
method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PHONE_STATE}) public boolean isTtySupported();
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean isVoiceMailNumber(android.telecom.PhoneAccountHandle, String);
method @RequiresPermission(anyOf={android.Manifest.permission.CALL_PHONE, android.Manifest.permission.MANAGE_OWN_CALLS}) public void placeCall(android.net.Uri, android.os.Bundle);
+ method public void registerCallEndpoints(@NonNull java.util.Set<android.telecom.CallEndpoint>);
method public void registerPhoneAccount(android.telecom.PhoneAccount);
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void showInCallScreen(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void silenceRinger();
method @RequiresPermission(android.Manifest.permission.CALL_PHONE) public void startConference(@NonNull java.util.List<android.net.Uri>, @NonNull android.os.Bundle);
+ method public void unregisterCallEndpoints(@NonNull java.util.Set<android.telecom.CallEndpoint>);
method public void unregisterPhoneAccount(android.telecom.PhoneAccountHandle);
field public static final String ACTION_CHANGE_DEFAULT_DIALER = "android.telecom.action.CHANGE_DEFAULT_DIALER";
field public static final String ACTION_CHANGE_PHONE_ACCOUNTS = "android.telecom.action.CHANGE_PHONE_ACCOUNTS";
@@ -41328,6 +41534,7 @@ package android.telecom {
field public static final String EXTRA_PHONE_ACCOUNT_HANDLE = "android.telecom.extra.PHONE_ACCOUNT_HANDLE";
field public static final String EXTRA_PICTURE_URI = "android.telecom.extra.PICTURE_URI";
field public static final String EXTRA_PRIORITY = "android.telecom.extra.PRIORITY";
+ field public static final String EXTRA_START_CALL_ON_ENDPOINT = "android.telecom.extra.START_CALL_ON_ENDPOINT";
field public static final String EXTRA_START_CALL_WITH_RTT = "android.telecom.extra.START_CALL_WITH_RTT";
field public static final String EXTRA_START_CALL_WITH_SPEAKERPHONE = "android.telecom.extra.START_CALL_WITH_SPEAKERPHONE";
field public static final String EXTRA_START_CALL_WITH_VIDEO_STATE = "android.telecom.extra.START_CALL_WITH_VIDEO_STATE";
@@ -41339,6 +41546,7 @@ package android.telecom {
field public static final String METADATA_IN_CALL_SERVICE_CAR_MODE_UI = "android.telecom.IN_CALL_SERVICE_CAR_MODE_UI";
field public static final String METADATA_IN_CALL_SERVICE_RINGING = "android.telecom.IN_CALL_SERVICE_RINGING";
field public static final String METADATA_IN_CALL_SERVICE_UI = "android.telecom.IN_CALL_SERVICE_UI";
+ field public static final String METADATA_STREAMING_TETHERED_CALLS = "android.telecom.STREAMING_TETHERED_CALLS";
field public static final int PRESENTATION_ALLOWED = 1; // 0x1
field public static final int PRESENTATION_PAYPHONE = 4; // 0x4
field public static final int PRESENTATION_RESTRICTED = 2; // 0x2
@@ -41705,11 +41913,11 @@ package android.telephony {
field public static final String KEY_CARRIER_SUPPORTS_SS_OVER_UT_BOOL = "carrier_supports_ss_over_ut_bool";
field public static final String KEY_CARRIER_USE_IMS_FIRST_FOR_EMERGENCY_BOOL = "carrier_use_ims_first_for_emergency_bool";
field public static final String KEY_CARRIER_USSD_METHOD_INT = "carrier_ussd_method_int";
- field public static final String KEY_CARRIER_UT_PROVISIONING_REQUIRED_BOOL = "carrier_ut_provisioning_required_bool";
+ field @Deprecated public static final String KEY_CARRIER_UT_PROVISIONING_REQUIRED_BOOL = "carrier_ut_provisioning_required_bool";
field public static final String KEY_CARRIER_VOLTE_AVAILABLE_BOOL = "carrier_volte_available_bool";
field public static final String KEY_CARRIER_VOLTE_OVERRIDE_WFC_PROVISIONING_BOOL = "carrier_volte_override_wfc_provisioning_bool";
- field public static final String KEY_CARRIER_VOLTE_PROVISIONED_BOOL = "carrier_volte_provisioned_bool";
- field public static final String KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL = "carrier_volte_provisioning_required_bool";
+ field @Deprecated public static final String KEY_CARRIER_VOLTE_PROVISIONED_BOOL = "carrier_volte_provisioned_bool";
+ field @Deprecated public static final String KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL = "carrier_volte_provisioning_required_bool";
field public static final String KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL = "carrier_volte_tty_supported_bool";
field public static final String KEY_CARRIER_VT_AVAILABLE_BOOL = "carrier_vt_available_bool";
field @Deprecated public static final String KEY_CARRIER_VVM_PACKAGE_NAME_STRING = "carrier_vvm_package_name_string";
@@ -41955,6 +42163,13 @@ package android.telephony {
field public static final int IPSEC_ENCRYPTION_ALGORITHM_AES_CBC = 2; // 0x2
field public static final int IPSEC_ENCRYPTION_ALGORITHM_DES_EDE3_CBC = 1; // 0x1
field public static final int IPSEC_ENCRYPTION_ALGORITHM_NULL = 0; // 0x0
+ field public static final String KEY_CAPABILITY_CALL_COMPOSER_INT_ARRAY = "ims.key_capability_type_call_composer_int_array";
+ field public static final String KEY_CAPABILITY_TYPE_OPTIONS_UCE_INT_ARRAY = "ims.key_capability_type_options_uce_int_array";
+ field public static final String KEY_CAPABILITY_TYPE_PRESENCE_UCE_INT_ARRAY = "ims.key_capability_type_presence_uce_int_array";
+ field public static final String KEY_CAPABILITY_TYPE_SMS_INT_ARRAY = "ims.key_capability_type_sms_int_array";
+ field public static final String KEY_CAPABILITY_TYPE_UT_INT_ARRAY = "ims.key_capability_type_ut_int_array";
+ field public static final String KEY_CAPABILITY_TYPE_VIDEO_INT_ARRAY = "ims.key_capability_type_video_int_array";
+ field public static final String KEY_CAPABILITY_TYPE_VOICE_INT_ARRAY = "ims.key_capability_type_voice_int_array";
field public static final String KEY_ENABLE_PRESENCE_CAPABILITY_EXCHANGE_BOOL = "ims.enable_presence_capability_exchange_bool";
field public static final String KEY_ENABLE_PRESENCE_GROUP_SUBSCRIBE_BOOL = "ims.enable_presence_group_subscribe_bool";
field public static final String KEY_ENABLE_PRESENCE_PUBLISH_BOOL = "ims.enable_presence_publish_bool";
@@ -41969,11 +42184,13 @@ package android.telephony {
field public static final String KEY_IPV4_SIP_MTU_SIZE_CELLULAR_INT = "ims.ipv4_sip_mtu_size_cellular_int";
field public static final String KEY_IPV6_SIP_MTU_SIZE_CELLULAR_INT = "ims.ipv6_sip_mtu_size_cellular_int";
field public static final String KEY_KEEP_PDN_UP_IN_NO_VOPS_BOOL = "ims.keep_pdn_up_in_no_vops_bool";
+ field public static final String KEY_MMTEL_REQUIRES_PROVISIONING_BUNDLE = "ims.mmtel_requires_provisioning_bundle";
field public static final String KEY_NON_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC_INT = "ims.non_rcs_capabilities_cache_expiration_sec_int";
field public static final String KEY_PHONE_CONTEXT_DOMAIN_NAME_STRING = "ims.phone_context_domain_name_string";
field public static final String KEY_PREFIX = "ims.";
field public static final String KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL = "ims.rcs_bulk_capability_exchange_bool";
field public static final String KEY_RCS_FEATURE_TAG_ALLOWED_STRING_ARRAY = "ims.rcs_feature_tag_allowed_string_array";
+ field public static final String KEY_RCS_REQUIRES_PROVISIONING_BUNDLE = "ims.rcs_requires_provisioning_bundle";
field public static final String KEY_REGISTRATION_EVENT_PACKAGE_SUPPORTED_BOOL = "ims.registration_event_package_supported_bool";
field public static final String KEY_REGISTRATION_EXPIRY_TIMER_SEC_INT = "ims.registration_expiry_timer_sec_int";
field public static final String KEY_REGISTRATION_RETRY_BASE_TIMER_MILLIS_INT = "ims.registration_retry_base_timer_millis_int";
@@ -42215,7 +42432,6 @@ package android.telephony {
field public static final String KEY_CHILD_SESSION_AES_CTR_KEY_SIZE_INT_ARRAY = "iwlan.child_session_aes_ctr_key_size_int_array";
field public static final String KEY_DIFFIE_HELLMAN_GROUPS_INT_ARRAY = "iwlan.diffie_hellman_groups_int_array";
field public static final String KEY_DPD_TIMER_SEC_INT = "iwlan.dpd_timer_sec_int";
- field public static final String KEY_ENABLE_SUPPORT_FOR_EAP_AKA_FAST_REAUTH_BOOL = "iwlan.enable_support_for_eap_aka_fast_reauth_bool";
field public static final String KEY_EPDG_ADDRESS_PRIORITY_INT_ARRAY = "iwlan.epdg_address_priority_int_array";
field public static final String KEY_EPDG_AUTHENTICATION_METHOD_INT = "iwlan.epdg_authentication_method_int";
field public static final String KEY_EPDG_PCO_ID_IPV4_INT = "iwlan.epdg_pco_id_ipv4_int";
@@ -42237,6 +42453,7 @@ package android.telephony {
field public static final String KEY_SUPPORTED_IKE_SESSION_ENCRYPTION_ALGORITHMS_INT_ARRAY = "iwlan.supported_ike_session_encryption_algorithms_int_array";
field public static final String KEY_SUPPORTED_INTEGRITY_ALGORITHMS_INT_ARRAY = "iwlan.supported_integrity_algorithms_int_array";
field public static final String KEY_SUPPORTED_PRF_ALGORITHMS_INT_ARRAY = "iwlan.supported_prf_algorithms_int_array";
+ field public static final String KEY_SUPPORTS_EAP_AKA_FAST_REAUTH_BOOL = "iwlan.supports_eap_aka_fast_reauth_bool";
}
public abstract class CellIdentity implements android.os.Parcelable {
@@ -44622,6 +44839,7 @@ package android.telephony.ims {
public class ImsManager {
method @NonNull public android.telephony.ims.ImsMmTelManager getImsMmTelManager(int);
method @NonNull public android.telephony.ims.ImsRcsManager getImsRcsManager(int);
+ method @NonNull public android.telephony.ims.ProvisioningManager getProvisioningManager(int);
field public static final String ACTION_WFC_IMS_REGISTRATION_ERROR = "android.telephony.ims.action.WFC_IMS_REGISTRATION_ERROR";
field public static final String EXTRA_WFC_REGISTRATION_FAILURE_MESSAGE = "android.telephony.ims.extra.WFC_REGISTRATION_FAILURE_MESSAGE";
field public static final String EXTRA_WFC_REGISTRATION_FAILURE_TITLE = "android.telephony.ims.extra.WFC_REGISTRATION_FAILURE_TITLE";
@@ -44872,6 +45090,23 @@ package android.telephony.ims {
field public static final int REASON_UNKNOWN_TEMPORARY_ERROR = 1; // 0x1
}
+ public class ProvisioningManager {
+ method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) @WorkerThread public boolean getProvisioningStatusForCapability(int, int);
+ method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) @WorkerThread public boolean getRcsProvisioningStatusForCapability(int, int);
+ method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public boolean isProvisioningRequiredForCapability(int, int);
+ method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public boolean isRcsProvisioningRequiredForCapability(int, int);
+ method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void registerFeatureProvisioningChangedCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ProvisioningManager.FeatureProvisioningCallback) throws android.telephony.ims.ImsException;
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setProvisioningStatusForCapability(int, int, boolean);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setRcsProvisioningStatusForCapability(int, int, boolean);
+ method public void unregisterFeatureProvisioningChangedCallback(@NonNull android.telephony.ims.ProvisioningManager.FeatureProvisioningCallback);
+ }
+
+ public static class ProvisioningManager.FeatureProvisioningCallback {
+ ctor public ProvisioningManager.FeatureProvisioningCallback();
+ method public void onFeatureProvisioningChanged(int, int, boolean);
+ method public void onRcsFeatureProvisioningChanged(int, int, boolean);
+ }
+
public class RcsUceAdapter {
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean isUceSettingEnabled() throws android.telephony.ims.ImsException;
}
@@ -44912,6 +45147,27 @@ package android.telephony.ims.feature {
field public static final int CAPABILITY_TYPE_VOICE = 1; // 0x1
}
+ public class RcsFeature {
+ }
+
+ public static class RcsFeature.RcsImsCapabilities {
+ field public static final int CAPABILITY_TYPE_NONE = 0; // 0x0
+ field public static final int CAPABILITY_TYPE_OPTIONS_UCE = 1; // 0x1
+ field public static final int CAPABILITY_TYPE_PRESENCE_UCE = 2; // 0x2
+ }
+
+}
+
+package android.telephony.ims.stub {
+
+ public class ImsRegistrationImplBase {
+ field public static final int REGISTRATION_TECH_CROSS_SIM = 2; // 0x2
+ field public static final int REGISTRATION_TECH_IWLAN = 1; // 0x1
+ field public static final int REGISTRATION_TECH_LTE = 0; // 0x0
+ field public static final int REGISTRATION_TECH_NONE = -1; // 0xffffffff
+ field public static final int REGISTRATION_TECH_NR = 3; // 0x3
+ }
+
}
package android.telephony.mbms {
@@ -48473,7 +48729,7 @@ package android.view {
field public static final int CLOCK_TICK = 4; // 0x4
field public static final int CONFIRM = 16; // 0x10
field public static final int CONTEXT_CLICK = 6; // 0x6
- field public static final int FLAG_IGNORE_GLOBAL_SETTING = 2; // 0x2
+ field @Deprecated public static final int FLAG_IGNORE_GLOBAL_SETTING = 2; // 0x2
field public static final int FLAG_IGNORE_VIEW_SETTING = 1; // 0x1
field public static final int GESTURE_END = 13; // 0xd
field public static final int GESTURE_START = 12; // 0xc
@@ -48503,6 +48759,7 @@ package android.view {
method public static int[] getDeviceIds();
method public int getId();
method public android.view.KeyCharacterMap getKeyCharacterMap();
+ method public int getKeyCodeForKeyLocation(int);
method public int getKeyboardType();
method @NonNull public android.hardware.lights.LightsManager getLightsManager();
method public android.view.InputDevice.MotionRange getMotionRange(int);
@@ -49449,6 +49706,22 @@ package android.view {
field public int toolType;
}
+ public interface OnBackInvokedCallback {
+ method public default void onBackInvoked();
+ }
+
+ public abstract class OnBackInvokedDispatcher {
+ ctor public OnBackInvokedDispatcher();
+ method public abstract void registerOnBackInvokedCallback(@NonNull android.view.OnBackInvokedCallback, int);
+ method public abstract void unregisterOnBackInvokedCallback(@NonNull android.view.OnBackInvokedCallback);
+ field public static final int PRIORITY_DEFAULT = 0; // 0x0
+ field public static final int PRIORITY_OVERLAY = 1000000; // 0xf4240
+ }
+
+ public interface OnBackInvokedDispatcherOwner {
+ method @Nullable public android.view.OnBackInvokedDispatcher getOnBackInvokedDispatcher();
+ }
+
public interface OnReceiveContentListener {
method @Nullable public android.view.ContentInfo onReceiveContent(@NonNull android.view.View, @NonNull android.view.ContentInfo);
}
@@ -49872,7 +50145,7 @@ package android.view {
field @NonNull public static final android.os.Parcelable.Creator<android.view.VerifiedMotionEvent> CREATOR;
}
- @UiThread public class View implements android.view.accessibility.AccessibilityEventSource android.graphics.drawable.Drawable.Callback android.view.KeyEvent.Callback {
+ @UiThread public class View implements android.view.accessibility.AccessibilityEventSource android.graphics.drawable.Drawable.Callback android.view.KeyEvent.Callback android.view.OnBackInvokedDispatcherOwner {
ctor public View(android.content.Context);
ctor public View(android.content.Context, @Nullable android.util.AttributeSet);
ctor public View(android.content.Context, @Nullable android.util.AttributeSet, int);
@@ -50076,6 +50349,7 @@ package android.view {
method @IdRes public int getNextFocusLeftId();
method @IdRes public int getNextFocusRightId();
method @IdRes public int getNextFocusUpId();
+ method @Nullable public android.view.OnBackInvokedDispatcher getOnBackInvokedDispatcher();
method public android.view.View.OnFocusChangeListener getOnFocusChangeListener();
method @ColorInt public int getOutlineAmbientShadowColor();
method public android.view.ViewOutlineProvider getOutlineProvider();
@@ -50093,6 +50367,7 @@ package android.view {
method public float getPivotX();
method public float getPivotY();
method public android.view.PointerIcon getPointerIcon();
+ method @NonNull public final java.util.List<android.graphics.Rect> getPreferKeepClearRects();
method @Nullable public String[] getReceiveContentMimeTypes();
method public android.content.res.Resources getResources();
method public final boolean getRevealOnFocusHint();
@@ -50210,6 +50485,7 @@ package android.view {
method protected boolean isPaddingOffsetRequired();
method public boolean isPaddingRelative();
method public boolean isPivotSet();
+ method public final boolean isPreferKeepClear();
method public boolean isPressed();
method public boolean isSaveEnabled();
method public boolean isSaveFromParentEnabled();
@@ -50452,6 +50728,8 @@ package android.view {
method public void setPivotX(float);
method public void setPivotY(float);
method public void setPointerIcon(android.view.PointerIcon);
+ method public final void setPreferKeepClear(boolean);
+ method public final void setPreferKeepClearRects(@NonNull java.util.List<android.graphics.Rect>);
method public void setPressed(boolean);
method public void setRenderEffect(@Nullable android.graphics.RenderEffect);
method public final void setRevealOnFocusHint(boolean);
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 4860a72ca67e..d22686c15e9b 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -9,7 +9,7 @@ package android {
package android.app {
- @UiContext public class Activity extends android.view.ContextThemeWrapper implements android.content.ComponentCallbacks2 android.view.KeyEvent.Callback android.view.LayoutInflater.Factory2 android.view.View.OnCreateContextMenuListener android.view.Window.Callback {
+ @UiContext public class Activity extends android.view.ContextThemeWrapper implements android.content.ComponentCallbacks2 android.view.KeyEvent.Callback android.view.LayoutInflater.Factory2 android.view.OnBackInvokedDispatcherOwner android.view.View.OnCreateContextMenuListener android.view.Window.Callback {
method public final boolean addDumpable(@NonNull android.util.Dumpable);
}
@@ -73,7 +73,30 @@ package android.app.admin {
package android.app.usage {
public class NetworkStatsManager {
+ method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public void forceUpdate();
method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public void notifyNetworkStatus(@NonNull java.util.List<android.net.Network>, @NonNull java.util.List<android.net.NetworkStateSnapshot>, @Nullable String, @NonNull java.util.List<android.net.UnderlyingNetworkInfo>);
+ method @NonNull @WorkerThread public android.app.usage.NetworkStats queryDetailsForDevice(@NonNull android.net.NetworkTemplate, long, long);
+ method @NonNull @WorkerThread public android.app.usage.NetworkStats queryDetailsForUidTagState(@NonNull android.net.NetworkTemplate, long, long, int, int, int) throws java.lang.SecurityException;
+ method @NonNull @WorkerThread public android.app.usage.NetworkStats querySummary(@NonNull android.net.NetworkTemplate, long, long) throws java.lang.SecurityException;
+ method @NonNull @WorkerThread public android.app.usage.NetworkStats.Bucket querySummaryForDevice(@NonNull android.net.NetworkTemplate, long, long);
+ method @NonNull @WorkerThread public android.app.usage.NetworkStats queryTaggedSummary(@NonNull android.net.NetworkTemplate, long, long) throws java.lang.SecurityException;
+ method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public void setDefaultGlobalAlert(long);
+ method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public void setPollOnOpen(boolean);
+ method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public void setStatsProviderWarningAndLimitAsync(@NonNull String, long, long);
+ method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public void setUidForeground(int, boolean);
+ }
+
+}
+
+package android.bluetooth {
+
+ public class BluetoothFrameworkInitializer {
+ method public static void registerServiceWrappers();
+ method public static void setBluetoothServiceManager(@NonNull android.os.BluetoothServiceManager);
+ }
+
+ public final class BluetoothPan implements android.bluetooth.BluetoothProfile {
+ method @Nullable @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED, android.Manifest.permission.TETHER_PRIVILEGED}) public android.net.TetheringManager.TetheredInterfaceRequest requestTetheredInterface(@NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.TetheredInterfaceCallback);
}
}
@@ -131,10 +154,12 @@ package android.hardware.usb {
field public static final int USB_DATA_TRANSFER_RATE_LOW_SPEED = 2; // 0x2
field public static final int USB_DATA_TRANSFER_RATE_UNKNOWN = -1; // 0xffffffff
field public static final int USB_HAL_NOT_SUPPORTED = -1; // 0xffffffff
+ field public static final int USB_HAL_RETRY = -2; // 0xfffffffe
field public static final int USB_HAL_V1_0 = 10; // 0xa
field public static final int USB_HAL_V1_1 = 11; // 0xb
field public static final int USB_HAL_V1_2 = 12; // 0xc
field public static final int USB_HAL_V1_3 = 13; // 0xd
+ field public static final int USB_HAL_V2_0 = 20; // 0x14
}
}
@@ -248,11 +273,39 @@ package android.net {
method public int getResourceId();
}
+ public class NetworkIdentity {
+ method public int getOemManaged();
+ method public int getRatType();
+ method @Nullable public String getSubscriberId();
+ method public int getType();
+ method @Nullable public String getWifiNetworkKey();
+ method public boolean isDefaultNetwork();
+ method public boolean isMetered();
+ method public boolean isRoaming();
+ }
+
+ public static final class NetworkIdentity.Builder {
+ ctor public NetworkIdentity.Builder();
+ method @NonNull public android.net.NetworkIdentity build();
+ method @NonNull public android.net.NetworkIdentity.Builder clearRatType();
+ method @NonNull public android.net.NetworkIdentity.Builder setDefaultNetwork(boolean);
+ method @NonNull public android.net.NetworkIdentity.Builder setMetered(boolean);
+ method @NonNull public android.net.NetworkIdentity.Builder setNetworkStateSnapshot(@NonNull android.net.NetworkStateSnapshot);
+ method @NonNull public android.net.NetworkIdentity.Builder setOemManaged(int);
+ method @NonNull public android.net.NetworkIdentity.Builder setRatType(int);
+ method @NonNull public android.net.NetworkIdentity.Builder setRoaming(boolean);
+ method @NonNull public android.net.NetworkIdentity.Builder setSubscriberId(@Nullable String);
+ method @NonNull public android.net.NetworkIdentity.Builder setType(int);
+ method @NonNull public android.net.NetworkIdentity.Builder setWifiNetworkKey(@Nullable String);
+ }
+
public class NetworkPolicyManager {
method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public int getMultipathPreference(@NonNull android.net.Network);
method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public int getRestrictBackgroundStatus(int);
+ method @Nullable @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public android.telephony.SubscriptionPlan getSubscriptionPlan(@NonNull android.net.NetworkTemplate);
method @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) public boolean isUidNetworkingBlocked(int, boolean);
method @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) public boolean isUidRestrictedOnMeteredNetworks(int);
+ method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public void notifyStatsProviderWarningOrLimitReached();
method @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) public void registerNetworkPolicyCallback(@Nullable java.util.concurrent.Executor, @NonNull android.net.NetworkPolicyManager.NetworkPolicyCallback);
method @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) public void unregisterNetworkPolicyCallback(@NonNull android.net.NetworkPolicyManager.NetworkPolicyCallback);
}
@@ -273,6 +326,30 @@ package android.net {
field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkStateSnapshot> CREATOR;
}
+ public final class NetworkStatsHistory implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public java.util.List<android.net.NetworkStatsHistory.Entry> getEntries();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkStatsHistory> CREATOR;
+ }
+
+ public static final class NetworkStatsHistory.Builder {
+ ctor public NetworkStatsHistory.Builder(long, int);
+ method @NonNull public android.net.NetworkStatsHistory.Builder addEntry(@NonNull android.net.NetworkStatsHistory.Entry);
+ method @NonNull public android.net.NetworkStatsHistory build();
+ }
+
+ public static final class NetworkStatsHistory.Entry {
+ ctor public NetworkStatsHistory.Entry(long, long, long, long, long, long, long);
+ method public long getActiveTime();
+ method public long getBucketStart();
+ method public long getOperations();
+ method public long getRxBytes();
+ method public long getRxPackets();
+ method public long getTxBytes();
+ method public long getTxPackets();
+ }
+
public final class NetworkTemplate implements android.os.Parcelable {
method public int describeContents();
method public int getDefaultNetworkStatus();
@@ -328,6 +405,10 @@ package android.net {
method public static void setHttpProxyConfiguration(@Nullable android.net.ProxyInfo);
}
+ public class TrafficStats {
+ method public static void init(@NonNull android.content.Context);
+ }
+
public final class UnderlyingNetworkInfo implements android.os.Parcelable {
ctor public UnderlyingNetworkInfo(int, @NonNull String, @NonNull java.util.List<java.lang.String>);
method public int describeContents();
@@ -358,6 +439,21 @@ package android.os {
method public final void markVintfStability();
}
+ public class BluetoothServiceManager {
+ method @NonNull public android.os.BluetoothServiceManager.ServiceRegisterer getBluetoothManagerServiceRegisterer();
+ }
+
+ public static class BluetoothServiceManager.ServiceNotFoundException extends java.lang.Exception {
+ ctor public BluetoothServiceManager.ServiceNotFoundException(@NonNull String);
+ }
+
+ public static final class BluetoothServiceManager.ServiceRegisterer {
+ method @Nullable public android.os.IBinder get();
+ method @NonNull public android.os.IBinder getOrThrow() throws android.os.BluetoothServiceManager.ServiceNotFoundException;
+ method public void register(@NonNull android.os.IBinder);
+ method @Nullable public android.os.IBinder tryGet();
+ }
+
public class Build {
method public static boolean isDebuggable();
}
@@ -371,6 +467,10 @@ package android.os {
}
public class Process {
+ method public static final boolean isSupplemental(int);
+ method public static final int toAppUid(int);
+ method public static final int toSupplementalUid(int);
+ field public static final int NFC_UID = 1027; // 0x403
field public static final int VPN_UID = 1016; // 0x3f8
}
@@ -402,6 +502,16 @@ package android.os {
method @NonNull public java.util.List<android.content.ComponentName> getEnabledComponentOverrides(@NonNull String);
}
+ public final class Trace {
+ method public static void asyncTraceBegin(long, @NonNull String, int);
+ method public static void asyncTraceEnd(long, @NonNull String, int);
+ method public static boolean isTagEnabled(long);
+ method public static void traceBegin(long, @NonNull String);
+ method public static void traceCounter(long, @NonNull String, int);
+ method public static void traceEnd(long);
+ field public static final long TRACE_TAG_NETWORK = 2097152L; // 0x200000L
+ }
+
}
package android.os.storage {
diff --git a/core/api/removed.txt b/core/api/removed.txt
index 07639fbf5378..311b110f1997 100644
--- a/core/api/removed.txt
+++ b/core/api/removed.txt
@@ -513,7 +513,7 @@ package android.util {
package android.view {
- @UiThread public class View implements android.view.accessibility.AccessibilityEventSource android.graphics.drawable.Drawable.Callback android.view.KeyEvent.Callback {
+ @UiThread public class View implements android.view.accessibility.AccessibilityEventSource android.graphics.drawable.Drawable.Callback android.view.KeyEvent.Callback android.view.OnBackInvokedDispatcherOwner {
method protected void initializeFadingEdge(android.content.res.TypedArray);
method protected void initializeScrollbars(android.content.res.TypedArray);
}
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 949dee3d6942..da4147b97550 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -2,12 +2,14 @@
package android {
public static final class Manifest.permission {
+ field public static final String ACCESS_AMBIENT_CONTEXT_EVENT = "android.permission.ACCESS_AMBIENT_CONTEXT_EVENT";
field public static final String ACCESS_AMBIENT_LIGHT_STATS = "android.permission.ACCESS_AMBIENT_LIGHT_STATS";
field public static final String ACCESS_BROADCAST_RADIO = "android.permission.ACCESS_BROADCAST_RADIO";
field public static final String ACCESS_CACHE_FILESYSTEM = "android.permission.ACCESS_CACHE_FILESYSTEM";
field public static final String ACCESS_CONTEXT_HUB = "android.permission.ACCESS_CONTEXT_HUB";
field public static final String ACCESS_DRM_CERTIFICATES = "android.permission.ACCESS_DRM_CERTIFICATES";
field @Deprecated public static final String ACCESS_FM_RADIO = "android.permission.ACCESS_FM_RADIO";
+ field public static final String ACCESS_FPS_COUNTER = "android.permission.ACCESS_FPS_COUNTER";
field public static final String ACCESS_INSTANT_APPS = "android.permission.ACCESS_INSTANT_APPS";
field public static final String ACCESS_LOCUS_ID_USAGE_STATS = "android.permission.ACCESS_LOCUS_ID_USAGE_STATS";
field public static final String ACCESS_MOCK_LOCATION = "android.permission.ACCESS_MOCK_LOCATION";
@@ -23,6 +25,7 @@ package android {
field public static final String ACCESS_TV_DESCRAMBLER = "android.permission.ACCESS_TV_DESCRAMBLER";
field public static final String ACCESS_TV_SHARED_FILTER = "android.permission.ACCESS_TV_SHARED_FILTER";
field public static final String ACCESS_TV_TUNER = "android.permission.ACCESS_TV_TUNER";
+ field public static final String ACCESS_ULTRASOUND = "android.permission.ACCESS_ULTRASOUND";
field public static final String ACCESS_VIBRATOR_STATE = "android.permission.ACCESS_VIBRATOR_STATE";
field public static final String ACTIVITY_EMBEDDING = "android.permission.ACTIVITY_EMBEDDING";
field public static final String ADJUST_RUNTIME_PERMISSIONS_POLICY = "android.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY";
@@ -36,6 +39,7 @@ package android {
field public static final String BACKGROUND_CAMERA = "android.permission.BACKGROUND_CAMERA";
field public static final String BACKUP = "android.permission.BACKUP";
field public static final String BATTERY_PREDICTION = "android.permission.BATTERY_PREDICTION";
+ field public static final String BIND_AMBIENT_CONTEXT_DETECTION_SERVICE = "android.permission.BIND_AMBIENT_CONTEXT_DETECTION_SERVICE";
field public static final String BIND_ATTENTION_SERVICE = "android.permission.BIND_ATTENTION_SERVICE";
field public static final String BIND_AUGMENTED_AUTOFILL_SERVICE = "android.permission.BIND_AUGMENTED_AUTOFILL_SERVICE";
field public static final String BIND_CALL_DIAGNOSTIC_SERVICE = "android.permission.BIND_CALL_DIAGNOSTIC_SERVICE";
@@ -138,6 +142,7 @@ package android {
field public static final String INTERNAL_SYSTEM_WINDOW = "android.permission.INTERNAL_SYSTEM_WINDOW";
field public static final String INVOKE_CARRIER_SETUP = "android.permission.INVOKE_CARRIER_SETUP";
field public static final String KILL_UID = "android.permission.KILL_UID";
+ field public static final String LAUNCH_DEVICE_MANAGER_SETUP = "android.permission.LAUNCH_DEVICE_MANAGER_SETUP";
field public static final String LOCAL_MAC_ADDRESS = "android.permission.LOCAL_MAC_ADDRESS";
field public static final String LOCK_DEVICE = "android.permission.LOCK_DEVICE";
field public static final String LOOP_RADIO = "android.permission.LOOP_RADIO";
@@ -157,6 +162,7 @@ package android {
field public static final String MANAGE_DEBUGGING = "android.permission.MANAGE_DEBUGGING";
field public static final String MANAGE_DEVICE_ADMINS = "android.permission.MANAGE_DEVICE_ADMINS";
field public static final String MANAGE_FACTORY_RESET_PROTECTION = "android.permission.MANAGE_FACTORY_RESET_PROTECTION";
+ field public static final String MANAGE_GAME_MODE = "android.permission.MANAGE_GAME_MODE";
field public static final String MANAGE_HOTWORD_DETECTION = "android.permission.MANAGE_HOTWORD_DETECTION";
field public static final String MANAGE_IPSEC_TUNNELS = "android.permission.MANAGE_IPSEC_TUNNELS";
field public static final String MANAGE_MUSIC_RECOGNITION = "android.permission.MANAGE_MUSIC_RECOGNITION";
@@ -296,6 +302,7 @@ package android {
field public static final String SET_SYSTEM_AUDIO_CAPTION = "android.permission.SET_SYSTEM_AUDIO_CAPTION";
field public static final String SET_VOLUME_KEY_LONG_PRESS_LISTENER = "android.permission.SET_VOLUME_KEY_LONG_PRESS_LISTENER";
field public static final String SET_WALLPAPER_COMPONENT = "android.permission.SET_WALLPAPER_COMPONENT";
+ field public static final String SET_WALLPAPER_DIM_AMOUNT = "android.permission.SET_WALLPAPER_DIM_AMOUNT";
field public static final String SHOW_KEYGUARD_MESSAGE = "android.permission.SHOW_KEYGUARD_MESSAGE";
field public static final String SHUTDOWN = "android.permission.SHUTDOWN";
field public static final String SIGNAL_REBOOT_READINESS = "android.permission.SIGNAL_REBOOT_READINESS";
@@ -364,6 +371,7 @@ package android {
}
public static final class R.bool {
+ field public static final int config_enableQrCodeScannerOnLockScreen;
field public static final int config_sendPackageName = 17891328; // 0x1110000
field public static final int config_showDefaultAssistant = 17891329; // 0x1110001
field public static final int config_showDefaultEmergency = 17891330; // 0x1110002
@@ -391,6 +399,7 @@ package android {
field public static final int config_customMediaKeyDispatcher = 17039404; // 0x104002c
field public static final int config_customMediaSessionPolicyProvider = 17039405; // 0x104002d
field public static final int config_defaultAssistant = 17039393; // 0x1040021
+ field public static final int config_defaultAutomotiveNavigation;
field public static final int config_defaultBrowser = 17039394; // 0x1040022
field public static final int config_defaultCallRedirection = 17039397; // 0x1040025
field public static final int config_defaultCallScreening = 17039398; // 0x1040026
@@ -443,7 +452,7 @@ package android.accounts {
package android.app {
- @UiContext public class Activity extends android.view.ContextThemeWrapper implements android.content.ComponentCallbacks2 android.view.KeyEvent.Callback android.view.LayoutInflater.Factory2 android.view.View.OnCreateContextMenuListener android.view.Window.Callback {
+ @UiContext public class Activity extends android.view.ContextThemeWrapper implements android.content.ComponentCallbacks2 android.view.KeyEvent.Callback android.view.LayoutInflater.Factory2 android.view.OnBackInvokedDispatcherOwner android.view.View.OnCreateContextMenuListener android.view.Window.Callback {
method public void convertFromTranslucent();
method public boolean convertToTranslucent(android.app.Activity.TranslucentConversionListener, android.app.ActivityOptions);
method @Deprecated public boolean isBackgroundVisibleBehind();
@@ -750,7 +759,17 @@ package android.app {
}
public final class GameManager {
- method @RequiresPermission("android.permission.MANAGE_GAME_MODE") public void setGameMode(@NonNull String, int);
+ method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_GAME_MODE) public android.app.GameModeInfo getGameModeInfo(@NonNull String);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_GAME_MODE) public void setGameMode(@NonNull String, int);
+ }
+
+ public final class GameModeInfo implements android.os.Parcelable {
+ ctor public GameModeInfo(int, @NonNull int[]);
+ method public int describeContents();
+ method public int getActiveGameMode();
+ method @NonNull public int[] getAvailableGameModes();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.GameModeInfo> CREATOR;
}
public abstract class InstantAppResolverService extends android.app.Service {
@@ -919,15 +938,21 @@ package android.app {
method @RequiresPermission(android.Manifest.permission.READ_PROJECTION_STATE) public void addOnProjectionStateChangedListener(int, @NonNull java.util.concurrent.Executor, @NonNull android.app.UiModeManager.OnProjectionStateChangedListener);
method @RequiresPermission(android.Manifest.permission.ENTER_CAR_MODE_PRIORITIZED) public void enableCarMode(@IntRange(from=0) int, int);
method @RequiresPermission(android.Manifest.permission.READ_PROJECTION_STATE) public int getActiveProjectionTypes();
+ method @RequiresPermission(android.Manifest.permission.MODIFY_DAY_NIGHT_MODE) public int getNightModeCustomType();
method @NonNull @RequiresPermission(android.Manifest.permission.READ_PROJECTION_STATE) public java.util.Set<java.lang.String> getProjectingPackages(int);
method @RequiresPermission(value=android.Manifest.permission.TOGGLE_AUTOMOTIVE_PROJECTION, conditional=true) public boolean releaseProjection(int);
method @RequiresPermission(android.Manifest.permission.READ_PROJECTION_STATE) public void removeOnProjectionStateChangedListener(@NonNull android.app.UiModeManager.OnProjectionStateChangedListener);
method @RequiresPermission(value=android.Manifest.permission.TOGGLE_AUTOMOTIVE_PROJECTION, conditional=true) public boolean requestProjection(int);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_DAY_NIGHT_MODE) public boolean setNightModeActivatedForCustomMode(int, boolean);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_DAY_NIGHT_MODE) public void setNightModeCustomType(int);
field public static final String ACTION_ENTER_CAR_MODE_PRIORITIZED = "android.app.action.ENTER_CAR_MODE_PRIORITIZED";
field public static final String ACTION_EXIT_CAR_MODE_PRIORITIZED = "android.app.action.EXIT_CAR_MODE_PRIORITIZED";
field public static final int DEFAULT_PRIORITY = 0; // 0x0
field public static final String EXTRA_CALLING_PACKAGE = "android.app.extra.CALLING_PACKAGE";
field public static final String EXTRA_PRIORITY = "android.app.extra.PRIORITY";
+ field public static final int MODE_NIGHT_CUSTOM_TYPE_BEDTIME = 1; // 0x1
+ field public static final int MODE_NIGHT_CUSTOM_TYPE_SCHEDULE = 0; // 0x0
+ field public static final int MODE_NIGHT_CUSTOM_TYPE_UNKNOWN = -1; // 0xffffffff
field public static final int PROJECTION_TYPE_ALL = -1; // 0xffffffff
field public static final int PROJECTION_TYPE_AUTOMOTIVE = 1; // 0x1
field public static final int PROJECTION_TYPE_NONE = 0; // 0x0
@@ -985,8 +1010,10 @@ package android.app {
public class WallpaperManager {
method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public void clearWallpaper(int, int);
+ method @RequiresPermission(android.Manifest.permission.SET_WALLPAPER_DIM_AMOUNT) public float getWallpaperDimAmount();
method public void setDisplayOffset(android.os.IBinder, int, int);
method @RequiresPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT) public boolean setWallpaperComponent(android.content.ComponentName);
+ method @RequiresPermission(android.Manifest.permission.SET_WALLPAPER_DIM_AMOUNT) public void setWallpaperDimAmount(@FloatRange(from=0.0f, to=1.0f) float);
}
}
@@ -1026,6 +1053,8 @@ package android.app.admin {
method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.QUERY_ADMIN_POLICY}) public java.util.List<java.lang.String> getPermittedInputMethodsForCurrentUser();
method @Nullable public android.content.ComponentName getProfileOwner() throws java.lang.IllegalArgumentException;
method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}) public String getProfileOwnerNameAsUser(int) throws java.lang.IllegalArgumentException;
+ method @NonNull public String getString(@NonNull String, @NonNull java.util.concurrent.Callable<java.lang.String>);
+ method @NonNull public String getString(@NonNull String, @NonNull java.util.concurrent.Callable<java.lang.String>, @NonNull java.lang.Object...);
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}) public int getUserProvisioningState();
method public boolean isDeviceManaged();
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isDeviceProvisioned();
@@ -1038,25 +1067,28 @@ package android.app.admin {
method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public boolean packageHasActiveAdmins(String);
method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void provisionFullyManagedDevice(@NonNull android.app.admin.FullyManagedDeviceProvisioningParams) throws android.app.admin.ProvisioningException;
method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES) public void resetDrawables(@NonNull int[]);
+ method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES) public void resetStrings(@NonNull String[]);
method @Deprecated @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_ADMINS) public boolean setActiveProfileOwner(@NonNull android.content.ComponentName, String) throws java.lang.IllegalArgumentException;
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void setDeviceProvisioningConfigApplied();
method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES) public void setDrawables(@NonNull java.util.Set<android.app.admin.DevicePolicyDrawableResource>);
method @Deprecated @RequiresPermission(value=android.Manifest.permission.GRANT_PROFILE_OWNER_DEVICE_IDS_ACCESS, conditional=true) public void setProfileOwnerCanAccessDeviceIds(@NonNull android.content.ComponentName);
method public void setSecondaryLockscreenEnabled(@NonNull android.content.ComponentName, boolean);
+ method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES) public void setStrings(@NonNull java.util.Set<android.app.admin.DevicePolicyStringResource>);
method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void setUserProvisioningState(int, @NonNull android.os.UserHandle);
field public static final String ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_ALLOWED = "android.account.DEVICE_OR_PROFILE_OWNER_ALLOWED";
field public static final String ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_DISALLOWED = "android.account.DEVICE_OR_PROFILE_OWNER_DISALLOWED";
field public static final String ACTION_BIND_SECONDARY_LOCKSCREEN_SERVICE = "android.app.action.BIND_SECONDARY_LOCKSCREEN_SERVICE";
+ field @RequiresPermission(android.Manifest.permission.DISPATCH_PROVISIONING_MESSAGE) public static final String ACTION_ESTABLISH_NETWORK_CONNECTION = "android.app.action.ESTABLISH_NETWORK_CONNECTION";
field public static final String ACTION_PROVISION_FINALIZATION = "android.app.action.PROVISION_FINALIZATION";
field public static final String ACTION_PROVISION_FINANCED_DEVICE = "android.app.action.PROVISION_FINANCED_DEVICE";
field public static final String ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE = "android.app.action.PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE";
field @RequiresPermission(android.Manifest.permission.MANAGE_FACTORY_RESET_PROTECTION) public static final String ACTION_RESET_PROTECTION_POLICY_CHANGED = "android.app.action.RESET_PROTECTION_POLICY_CHANGED";
- field @RequiresPermission("android.permission.LAUNCH_DEVICE_MANAGER_SETUP") public static final String ACTION_ROLE_HOLDER_PROVISION_FINALIZATION = "android.app.action.ROLE_HOLDER_PROVISION_FINALIZATION";
- field @RequiresPermission("android.permission.LAUNCH_DEVICE_MANAGER_SETUP") public static final String ACTION_ROLE_HOLDER_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE = "android.app.action.ROLE_HOLDER_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE";
- field @RequiresPermission("android.permission.LAUNCH_DEVICE_MANAGER_SETUP") public static final String ACTION_ROLE_HOLDER_PROVISION_MANAGED_PROFILE = "android.app.action.ROLE_HOLDER_PROVISION_MANAGED_PROFILE";
+ field @RequiresPermission(android.Manifest.permission.LAUNCH_DEVICE_MANAGER_SETUP) public static final String ACTION_ROLE_HOLDER_PROVISION_FINALIZATION = "android.app.action.ROLE_HOLDER_PROVISION_FINALIZATION";
+ field @RequiresPermission(android.Manifest.permission.LAUNCH_DEVICE_MANAGER_SETUP) public static final String ACTION_ROLE_HOLDER_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE = "android.app.action.ROLE_HOLDER_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE";
+ field @RequiresPermission(android.Manifest.permission.LAUNCH_DEVICE_MANAGER_SETUP) public static final String ACTION_ROLE_HOLDER_PROVISION_MANAGED_PROFILE = "android.app.action.ROLE_HOLDER_PROVISION_MANAGED_PROFILE";
field public static final String ACTION_SET_PROFILE_OWNER = "android.app.action.SET_PROFILE_OWNER";
field @Deprecated public static final String ACTION_STATE_USER_SETUP_COMPLETE = "android.app.action.STATE_USER_SETUP_COMPLETE";
- field @RequiresPermission("android.permission.LAUNCH_DEVICE_MANAGER_SETUP") public static final String ACTION_UPDATE_DEVICE_MANAGEMENT_ROLE_HOLDER = "android.app.action.UPDATE_DEVICE_MANAGEMENT_ROLE_HOLDER";
+ field @RequiresPermission(android.Manifest.permission.LAUNCH_DEVICE_MANAGER_SETUP) public static final String ACTION_UPDATE_DEVICE_MANAGEMENT_ROLE_HOLDER = "android.app.action.UPDATE_DEVICE_MANAGEMENT_ROLE_HOLDER";
field public static final int CODE_ACCOUNTS_NOT_EMPTY = 6; // 0x6
field public static final int CODE_CANNOT_ADD_MANAGED_PROFILE = 11; // 0xb
field public static final int CODE_DEVICE_ADMIN_NOT_SUPPORTED = 13; // 0xd
@@ -1110,6 +1142,48 @@ package android.app.admin {
field public static final int STATE_USER_UNMANAGED = 0; // 0x0
}
+ public static final class DevicePolicyResources.Strings {
+ field public static final String UNDEFINED = "UNDEFINED";
+ }
+
+ public static final class DevicePolicyResources.Strings.DocumentsUi {
+ field public static final String CANT_SAVE_TO_PERSONAL_MESSAGE = "DocumentsUi.CANT_SAVE_TO_PERSONAL_MESSAGE";
+ field public static final String CANT_SAVE_TO_PERSONAL_TITLE = "DocumentsUi.CANT_SAVE_TO_PERSONAL_TITLE";
+ field public static final String CANT_SAVE_TO_WORK_MESSAGE = "DocumentsUi.CANT_SAVE_TO_WORK_MESSAGE";
+ field public static final String CANT_SAVE_TO_WORK_TITLE = "DocumentsUi.CANT_SAVE_TO_WORK_TITLE";
+ field public static final String CANT_SELECT_PERSONAL_FILES_MESSAGE = "DocumentsUi.CANT_SELECT_PERSONAL_FILES_MESSAGE";
+ field public static final String CANT_SELECT_PERSONAL_FILES_TITLE = "DocumentsUi.CANT_SELECT_PERSONAL_FILES_TITLE";
+ field public static final String CANT_SELECT_WORK_FILES_MESSAGE = "DocumentsUi.CANT_SELECT_WORK_FILES_MESSAGE";
+ field public static final String CANT_SELECT_WORK_FILES_TITLE = "DocumentsUi.CANT_SELECT_WORK_FILES_TITLE";
+ field public static final String CROSS_PROFILE_NOT_ALLOWED_MESSAGE = "DocumentsUi.CROSS_PROFILE_NOT_ALLOWED_MESSAGE";
+ field public static final String CROSS_PROFILE_NOT_ALLOWED_TITLE = "DocumentsUi.CROSS_PROFILE_NOT_ALLOWED_TITLE";
+ field public static final String PERSONAL_TAB = "DocumentsUi.PERSONAL_TAB";
+ field public static final String PREVIEW_WORK_FILE_ACCESSIBILITY = "DocumentsUi.PREVIEW_WORK_FILE_ACCESSIBILITY";
+ field public static final String WORK_ACCESSIBILITY = "DocumentsUi.WORK_ACCESSIBILITY";
+ field public static final String WORK_PROFILE_OFF_ENABLE_BUTTON = "DocumentsUi.WORK_PROFILE_OFF_ENABLE_BUTTON";
+ field public static final String WORK_PROFILE_OFF_ERROR_TITLE = "DocumentsUi.WORK_PROFILE_OFF_ERROR_TITLE";
+ field public static final String WORK_TAB = "DocumentsUi.WORK_TAB";
+ }
+
+ public static final class DevicePolicyResources.Strings.MediaProvider {
+ field public static final String BLOCKED_BY_ADMIN_TITLE = "MediaProvider.BLOCKED_BY_ADMIN_TITLE";
+ field public static final String BLOCKED_FROM_PERSONAL_MESSAGE = "MediaProvider.BLOCKED_FROM_PERSONAL_MESSAGE";
+ field public static final String BLOCKED_FROM_WORK_MESSAGE = "MediaProvider.BLOCKED_FROM_WORK_MESSAGE";
+ field public static final String SWITCH_TO_PERSONAL_MESSAGE = "MediaProvider.SWITCH_TO_PERSONAL_MESSAGE";
+ field public static final String SWITCH_TO_WORK_MESSAGE = "MediaProvider.SWITCH_TO_WORK_MESSAGE";
+ field public static final String WORK_PROFILE_PAUSED_MESSAGE = "MediaProvider.WORK_PROFILE_PAUSED_MESSAGE";
+ field public static final String WORK_PROFILE_PAUSED_TITLE = "MediaProvider.WORK_PROFILE_PAUSED_TITLE";
+ }
+
+ public final class DevicePolicyStringResource implements android.os.Parcelable {
+ ctor public DevicePolicyStringResource(@NonNull android.content.Context, @NonNull String, @StringRes int);
+ method public int describeContents();
+ method public int getCallingPackageResourceId();
+ method @NonNull public String getStringId();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.admin.DevicePolicyStringResource> CREATOR;
+ }
+
public final class FullyManagedDeviceProvisioningParams implements android.os.Parcelable {
method public boolean canDeviceOwnerGrantSensorsPermissions();
method public int describeContents();
@@ -1181,6 +1255,87 @@ package android.app.admin {
}
+package android.app.ambientcontext {
+
+ public final class AmbientContextEvent implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getConfidenceLevel();
+ method public int getDensityLevel();
+ method @NonNull public java.time.Instant getEndTime();
+ method public int getEventType();
+ method @NonNull public java.time.Instant getStartTime();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.ambientcontext.AmbientContextEvent> CREATOR;
+ field public static final int EVENT_COUGH = 1; // 0x1
+ field public static final int EVENT_SNORE = 2; // 0x2
+ field public static final int EVENT_UNKNOWN = 0; // 0x0
+ field public static final int LEVEL_HIGH = 5; // 0x5
+ field public static final int LEVEL_LOW = 1; // 0x1
+ field public static final int LEVEL_MEDIUM = 3; // 0x3
+ field public static final int LEVEL_MEDIUM_HIGH = 4; // 0x4
+ field public static final int LEVEL_MEDIUM_LOW = 2; // 0x2
+ field public static final int LEVEL_UNKNOWN = 0; // 0x0
+ }
+
+ public static final class AmbientContextEvent.Builder {
+ ctor public AmbientContextEvent.Builder();
+ method @NonNull public android.app.ambientcontext.AmbientContextEvent build();
+ method @NonNull public android.app.ambientcontext.AmbientContextEvent.Builder setConfidenceLevel(int);
+ method @NonNull public android.app.ambientcontext.AmbientContextEvent.Builder setDensityLevel(int);
+ method @NonNull public android.app.ambientcontext.AmbientContextEvent.Builder setEndTime(@NonNull java.time.Instant);
+ method @NonNull public android.app.ambientcontext.AmbientContextEvent.Builder setEventType(int);
+ method @NonNull public android.app.ambientcontext.AmbientContextEvent.Builder setStartTime(@NonNull java.time.Instant);
+ }
+
+ public final class AmbientContextEventRequest implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public java.util.Set<java.lang.Integer> getEventTypes();
+ method @NonNull public android.os.PersistableBundle getOptions();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.ambientcontext.AmbientContextEventRequest> CREATOR;
+ }
+
+ public static final class AmbientContextEventRequest.Builder {
+ ctor public AmbientContextEventRequest.Builder();
+ method @NonNull public android.app.ambientcontext.AmbientContextEventRequest.Builder addEventType(int);
+ method @NonNull public android.app.ambientcontext.AmbientContextEventRequest build();
+ method @NonNull public android.app.ambientcontext.AmbientContextEventRequest.Builder setOptions(@NonNull android.os.PersistableBundle);
+ }
+
+ public final class AmbientContextEventResponse implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public android.app.PendingIntent getActionPendingIntent();
+ method @NonNull public java.util.List<android.app.ambientcontext.AmbientContextEvent> getEvents();
+ method @NonNull public String getPackageName();
+ method public int getStatusCode();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.ambientcontext.AmbientContextEventResponse> CREATOR;
+ field public static final int STATUS_ACCESS_DENIED = 5; // 0x5
+ field public static final int STATUS_MICROPHONE_DISABLED = 4; // 0x4
+ field public static final int STATUS_NOT_SUPPORTED = 2; // 0x2
+ field public static final int STATUS_SERVICE_UNAVAILABLE = 3; // 0x3
+ field public static final int STATUS_SUCCESS = 1; // 0x1
+ field public static final int STATUS_UNKNOWN = 0; // 0x0
+ }
+
+ public static final class AmbientContextEventResponse.Builder {
+ ctor public AmbientContextEventResponse.Builder();
+ method @NonNull public android.app.ambientcontext.AmbientContextEventResponse.Builder addEvent(@NonNull android.app.ambientcontext.AmbientContextEvent);
+ method @NonNull public android.app.ambientcontext.AmbientContextEventResponse build();
+ method @NonNull public android.app.ambientcontext.AmbientContextEventResponse.Builder setActionPendingIntent(@NonNull android.app.PendingIntent);
+ method @NonNull public android.app.ambientcontext.AmbientContextEventResponse.Builder setPackageName(@NonNull String);
+ method @NonNull public android.app.ambientcontext.AmbientContextEventResponse.Builder setStatusCode(int);
+ }
+
+ public final class AmbientContextManager {
+ method @Nullable public static android.app.ambientcontext.AmbientContextEventResponse getResponseFromIntent(@NonNull android.content.Intent);
+ method @RequiresPermission(android.Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT) public void registerObserver(@NonNull android.app.ambientcontext.AmbientContextEventRequest, @NonNull android.app.PendingIntent);
+ method @RequiresPermission(android.Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT) public void unregisterObserver();
+ field public static final String EXTRA_AMBIENT_CONTEXT_EVENT_RESPONSE = "android.app.ambientcontext.extra.AMBIENT_CONTEXT_EVENT_RESPONSE";
+ }
+
+}
+
package android.app.assist {
public class ActivityId {
@@ -2030,6 +2185,8 @@ package android.app.usage {
}
public class NetworkStatsManager {
+ method @NonNull @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public android.net.NetworkStats getMobileUidStats();
+ method @NonNull @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public android.net.NetworkStats getWifiUidStats();
method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STATS_PROVIDER, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void registerNetworkStatsProvider(@NonNull String, @NonNull android.net.netstats.provider.NetworkStatsProvider);
method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STATS_PROVIDER, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void unregisterNetworkStatsProvider(@NonNull android.net.netstats.provider.NetworkStatsProvider);
}
@@ -2206,6 +2363,7 @@ package android.bluetooth {
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean isEncrypted();
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean isInSilenceMode();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean removeBond();
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean setLowLatencyAudioAllowed(boolean);
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean setMessageAccessPermission(int);
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean setMetadata(int, @NonNull byte[]);
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean setPhonebookAccessPermission(int);
@@ -2258,6 +2416,15 @@ package android.bluetooth {
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.MODIFY_PHONE_STATE}) public boolean stopScoUsingVirtualVoiceCall();
}
+ public final class BluetoothHeadsetClient implements java.lang.AutoCloseable android.bluetooth.BluetoothProfile {
+ 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 int getConnectionState(@NonNull android.bluetooth.BluetoothDevice);
+ method @NonNull @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(@NonNull int[]);
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
+ field @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.headsetprofile.action.CONNECTION_STATE_CHANGED";
+ }
+
public final class BluetoothHearingAid implements android.bluetooth.BluetoothProfile {
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 long getHiSyncId(@NonNull android.bluetooth.BluetoothDevice);
@@ -2276,6 +2443,10 @@ package android.bluetooth {
field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.input.profile.action.CONNECTION_STATE_CHANGED";
}
+ public final class BluetoothLeAudio implements java.lang.AutoCloseable android.bluetooth.BluetoothProfile {
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getAudioLocation(@NonNull android.bluetooth.BluetoothDevice);
+ }
+
public final class BluetoothMap implements java.lang.AutoCloseable android.bluetooth.BluetoothProfile {
method public void close();
method protected void finalize();
@@ -2285,15 +2456,21 @@ package android.bluetooth {
field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.map.profile.action.CONNECTION_STATE_CHANGED";
}
- public final class BluetoothMapClient implements android.bluetooth.BluetoothProfile {
+ public final class BluetoothMapClient implements java.lang.AutoCloseable android.bluetooth.BluetoothProfile {
+ 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 int getConnectionState(@NonNull android.bluetooth.BluetoothDevice);
+ method @NonNull @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(@NonNull int[]);
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.SEND_SMS}) public boolean sendMessage(@NonNull android.bluetooth.BluetoothDevice, @NonNull java.util.Collection<android.net.Uri>, @NonNull String, @Nullable android.app.PendingIntent, @Nullable android.app.PendingIntent);
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
+ field @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.mapmce.profile.action.CONNECTION_STATE_CHANGED";
}
public final class BluetoothPan implements android.bluetooth.BluetoothProfile {
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 getConnectionState(@NonNull android.bluetooth.BluetoothDevice);
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean isTetheringOn();
- method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED, android.Manifest.permission.TETHER_PRIVILEGED}) public void setBluetoothTethering(boolean);
+ method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED, android.Manifest.permission.TETHER_PRIVILEGED}) public void setBluetoothTethering(boolean);
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.pan.profile.action.CONNECTION_STATE_CHANGED";
field public static final String ACTION_TETHERING_STATE_CHANGED = "android.bluetooth.action.TETHERING_STATE_CHANGED";
@@ -2314,6 +2491,15 @@ package android.bluetooth {
field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED";
}
+ public final class BluetoothPbapClient implements java.lang.AutoCloseable android.bluetooth.BluetoothProfile {
+ 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 int getConnectionState(@NonNull android.bluetooth.BluetoothDevice);
+ method @NonNull @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(@NonNull int[]);
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
+ field @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.pbapclient.profile.action.CONNECTION_STATE_CHANGED";
+ }
+
public interface BluetoothProfile {
field public static final int A2DP_SINK = 11; // 0xb
field public static final int AVRCP_CONTROLLER = 12; // 0xc
@@ -2355,6 +2541,7 @@ package android.bluetooth {
field @NonNull public static final android.os.ParcelUuid COORDINATED_SET;
field @NonNull public static final android.os.ParcelUuid DIP;
field @NonNull public static final android.os.ParcelUuid GENERIC_MEDIA_CONTROL;
+ field @NonNull public static final android.os.ParcelUuid HAS;
field @NonNull public static final android.os.ParcelUuid HEARING_AID;
field @NonNull public static final android.os.ParcelUuid HFP;
field @NonNull public static final android.os.ParcelUuid HFP_AG;
@@ -2571,10 +2758,32 @@ package android.companion {
package android.companion.virtual {
public final class VirtualDeviceManager {
+ method @Nullable @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.companion.virtual.VirtualDeviceManager.VirtualDevice createVirtualDevice(int, @NonNull android.companion.virtual.VirtualDeviceParams);
}
public static class VirtualDeviceManager.VirtualDevice implements java.lang.AutoCloseable {
method public void close();
+ method @Nullable public android.hardware.display.VirtualDisplay createVirtualDisplay(int, int, int, @Nullable android.view.Surface, int, @Nullable android.os.Handler, @Nullable android.hardware.display.VirtualDisplay.Callback);
+ method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualKeyboard createVirtualKeyboard(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int);
+ method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualMouse createVirtualMouse(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int);
+ method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualTouchscreen createVirtualTouchscreen(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int);
+ }
+
+ public final class VirtualDeviceParams implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getLockState();
+ method @NonNull public java.util.Set<android.os.UserHandle> getUsersWithMatchingAccounts();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.companion.virtual.VirtualDeviceParams> CREATOR;
+ field public static final int LOCK_STATE_ALWAYS_LOCKED = 0; // 0x0
+ field public static final int LOCK_STATE_ALWAYS_UNLOCKED = 1; // 0x1
+ }
+
+ public static final class VirtualDeviceParams.Builder {
+ ctor public VirtualDeviceParams.Builder();
+ method @NonNull public android.companion.virtual.VirtualDeviceParams build();
+ method @NonNull @RequiresPermission(value="android.permission.ADD_ALWAYS_UNLOCKED_DISPLAY", conditional=true) public android.companion.virtual.VirtualDeviceParams.Builder setLockState(int);
+ method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setUsersWithMatchingAccounts(@NonNull java.util.Set<android.os.UserHandle>);
}
}
@@ -2632,6 +2841,7 @@ package android.content {
method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public abstract void sendBroadcastAsUser(@RequiresPermission android.content.Intent, android.os.UserHandle, @Nullable String, @Nullable android.os.Bundle);
method public abstract void sendOrderedBroadcast(@NonNull android.content.Intent, @Nullable String, @Nullable android.os.Bundle, @Nullable android.content.BroadcastReceiver, @Nullable android.os.Handler, int, @Nullable String, @Nullable android.os.Bundle);
method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public void startActivityAsUser(@NonNull @RequiresPermission android.content.Intent, @NonNull android.os.UserHandle);
+ field public static final String AMBIENT_CONTEXT_SERVICE = "ambient_context";
field public static final String APP_HIBERNATION_SERVICE = "app_hibernation";
field public static final String APP_INTEGRITY_SERVICE = "app_integrity";
field public static final String APP_PREDICTION_SERVICE = "app_prediction";
@@ -3628,6 +3838,7 @@ package android.hardware.display {
method @RequiresPermission(android.Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) public void setBrightnessConfiguration(android.hardware.display.BrightnessConfiguration);
method @RequiresPermission(android.Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) public void setBrightnessConfigurationForDisplay(@NonNull android.hardware.display.BrightnessConfiguration, @NonNull String);
method @Deprecated @RequiresPermission(android.Manifest.permission.CONTROL_DISPLAY_SATURATION) public void setSaturationLevel(float);
+ field public static final int VIRTUAL_DISPLAY_FLAG_TRUSTED = 1024; // 0x400
}
}
@@ -4063,6 +4274,7 @@ package android.hardware.input {
public class VirtualMouse implements java.io.Closeable {
method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void close();
+ method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.graphics.PointF getCursorPosition();
method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void sendButtonEvent(@NonNull android.hardware.input.VirtualMouseButtonEvent);
method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void sendRelativeEvent(@NonNull android.hardware.input.VirtualMouseRelativeEvent);
method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void sendScrollEvent(@NonNull android.hardware.input.VirtualMouseScrollEvent);
@@ -5022,8 +5234,27 @@ package android.hardware.usb {
}
public final class UsbPort {
+ method @CheckResult @RequiresPermission(android.Manifest.permission.MANAGE_USB) public int enableLimitPowerTransfer(boolean);
+ method @CheckResult @RequiresPermission(android.Manifest.permission.MANAGE_USB) public int enableUsbData(boolean);
+ method @CheckResult @RequiresPermission(android.Manifest.permission.MANAGE_USB) public int enableUsbDataWhileDocked();
method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_USB) public android.hardware.usb.UsbPortStatus getStatus();
method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public void setRoles(int, int);
+ field public static final int ENABLE_LIMIT_POWER_TRANSFER_ERROR_INTERNAL = 1; // 0x1
+ field public static final int ENABLE_LIMIT_POWER_TRANSFER_ERROR_NOT_SUPPORTED = 2; // 0x2
+ field public static final int ENABLE_LIMIT_POWER_TRANSFER_ERROR_OTHER = 4; // 0x4
+ field public static final int ENABLE_LIMIT_POWER_TRANSFER_ERROR_PORT_MISMATCH = 3; // 0x3
+ field public static final int ENABLE_LIMIT_POWER_TRANSFER_SUCCESS = 0; // 0x0
+ field public static final int ENABLE_USB_DATA_ERROR_INTERNAL = 1; // 0x1
+ field public static final int ENABLE_USB_DATA_ERROR_NOT_SUPPORTED = 2; // 0x2
+ field public static final int ENABLE_USB_DATA_ERROR_OTHER = 4; // 0x4
+ field public static final int ENABLE_USB_DATA_ERROR_PORT_MISMATCH = 3; // 0x3
+ field public static final int ENABLE_USB_DATA_SUCCESS = 0; // 0x0
+ field public static final int ENABLE_USB_DATA_WHILE_DOCKED_ERROR_DATA_ENABLED = 4; // 0x4
+ field public static final int ENABLE_USB_DATA_WHILE_DOCKED_ERROR_INTERNAL = 1; // 0x1
+ field public static final int ENABLE_USB_DATA_WHILE_DOCKED_ERROR_NOT_SUPPORTED = 2; // 0x2
+ field public static final int ENABLE_USB_DATA_WHILE_DOCKED_ERROR_OTHER = 5; // 0x5
+ field public static final int ENABLE_USB_DATA_WHILE_DOCKED_ERROR_PORT_MISMATCH = 3; // 0x3
+ field public static final int ENABLE_USB_DATA_WHILE_DOCKED_SUCCESS = 0; // 0x0
}
public final class UsbPortStatus implements android.os.Parcelable {
@@ -5031,8 +5262,11 @@ package android.hardware.usb {
method public int getCurrentDataRole();
method public int getCurrentMode();
method public int getCurrentPowerRole();
+ method public int getPowerBrickStatus();
method public int getSupportedRoleCombinations();
+ method @Nullable public int[] getUsbDataStatus();
method public boolean isConnected();
+ method public boolean isPowerTransferLimited();
method public boolean isRoleCombinationSupported(int, int);
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.hardware.usb.UsbPortStatus> CREATOR;
@@ -5044,9 +5278,19 @@ package android.hardware.usb {
field public static final int MODE_DFP = 2; // 0x2
field public static final int MODE_NONE = 0; // 0x0
field public static final int MODE_UFP = 1; // 0x1
+ field public static final int POWER_BRICK_STATUS_CONNECTED = 1; // 0x1
+ field public static final int POWER_BRICK_STATUS_DISCONNECTED = 2; // 0x2
+ field public static final int POWER_BRICK_STATUS_UNKNOWN = 0; // 0x0
field public static final int POWER_ROLE_NONE = 0; // 0x0
field public static final int POWER_ROLE_SINK = 2; // 0x2
field public static final int POWER_ROLE_SOURCE = 1; // 0x1
+ field public static final int USB_DATA_STATUS_DISABLED_CONTAMINANT = 3; // 0x3
+ field public static final int USB_DATA_STATUS_DISABLED_DEBUG = 6; // 0x6
+ field public static final int USB_DATA_STATUS_DISABLED_DOCK = 4; // 0x4
+ field public static final int USB_DATA_STATUS_DISABLED_FORCE = 5; // 0x5
+ field public static final int USB_DATA_STATUS_DISABLED_OVERHEAT = 2; // 0x2
+ field public static final int USB_DATA_STATUS_ENABLED = 1; // 0x1
+ field public static final int USB_DATA_STATUS_UNKNOWN = 0; // 0x0
}
}
@@ -5663,6 +5907,7 @@ package android.media {
method public int getCapturePreset();
method public int getSystemUsage();
method public static boolean isSystemUsage(int);
+ field @RequiresPermission(android.Manifest.permission.ACCESS_ULTRASOUND) public static final int CONTENT_TYPE_ULTRASOUND = 1997; // 0x7cd
field public static final int FLAG_BEACON = 8; // 0x8
field public static final int FLAG_BYPASS_INTERRUPTION_POLICY = 64; // 0x40
field public static final int FLAG_BYPASS_MUTE = 128; // 0x80
@@ -5679,6 +5924,7 @@ package android.media {
method public android.media.AudioAttributes.Builder setCapturePreset(int);
method @NonNull @RequiresPermission(android.Manifest.permission.CAPTURE_AUDIO_HOTWORD) public android.media.AudioAttributes.Builder setHotwordModeEnabled(boolean);
method public android.media.AudioAttributes.Builder setInternalCapturePreset(int);
+ method @NonNull public android.media.AudioAttributes.Builder setInternalContentType(int);
method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public android.media.AudioAttributes.Builder setSystemUsage(int);
}
@@ -5894,6 +6140,7 @@ package android.media {
field @RequiresPermission(android.Manifest.permission.CAPTURE_AUDIO_OUTPUT) public static final int ECHO_REFERENCE = 1997; // 0x7cd
field @RequiresPermission(android.Manifest.permission.CAPTURE_AUDIO_HOTWORD) public static final int HOTWORD = 1999; // 0x7cf
field @RequiresPermission(android.Manifest.permission.CAPTURE_AUDIO_OUTPUT) public static final int RADIO_TUNER = 1998; // 0x7ce
+ field @RequiresPermission(android.Manifest.permission.ACCESS_ULTRASOUND) public static final int ULTRASOUND = 2000; // 0x7d0
}
public final class MediaRouter2 {
@@ -6559,6 +6806,7 @@ package android.media.tv.tuner {
method @Nullable public android.media.tv.tuner.Lnb openLnbByName(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.LnbCallback);
method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_SHARED_FILTER) public static android.media.tv.tuner.filter.SharedFilter openSharedFilter(@NonNull android.content.Context, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.filter.SharedFilterCallback);
method @Nullable public android.media.tv.tuner.filter.TimeFilter openTimeFilter();
+ method public int removeOutputPid(@IntRange(from=0) int);
method public int scan(@NonNull android.media.tv.tuner.frontend.FrontendSettings, int, @NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.frontend.ScanCallback);
method public int setLnaEnabled(boolean);
method public int setMaxNumberOfFrontends(int, @IntRange(from=0) int);
@@ -6999,7 +7247,7 @@ package android.media.tv.tuner.filter {
}
public abstract class SectionSettings extends android.media.tv.tuner.filter.Settings {
- method public int getBitWidthOfLengthField();
+ method public int getLengthFieldBitWidth();
method public boolean isCrcEnabled();
method public boolean isRaw();
method public boolean isRepeat();
@@ -7699,6 +7947,7 @@ package android.media.tv.tuner.frontend {
public class FrontendStatus {
method public int getAgc();
+ method @NonNull public android.media.tv.tuner.frontend.Atsc3PlpInfo[] getAllAtsc3PlpInfo();
method @NonNull public android.media.tv.tuner.frontend.FrontendStatus.Atsc3PlpTuningInfo[] getAtsc3PlpTuningInfo();
method public int getBandwidth();
method public int getBer();
@@ -7741,6 +7990,7 @@ package android.media.tv.tuner.frontend {
method public boolean isRfLocked();
method public boolean isShortFramesEnabled();
field public static final int FRONTEND_STATUS_TYPE_AGC = 14; // 0xe
+ field public static final int FRONTEND_STATUS_TYPE_ATSC3_ALL_PLP_INFO = 41; // 0x29
field public static final int FRONTEND_STATUS_TYPE_ATSC3_PLP_INFO = 21; // 0x15
field public static final int FRONTEND_STATUS_TYPE_BANDWIDTH = 25; // 0x19
field public static final int FRONTEND_STATUS_TYPE_BER = 2; // 0x2
@@ -8149,11 +8399,12 @@ package android.net {
field public static final String PERMISSION_MAINLINE_NETWORK_STACK = "android.permission.MAINLINE_NETWORK_STACK";
}
- public final class NetworkStats implements android.os.Parcelable {
+ public final class NetworkStats implements java.lang.Iterable<android.net.NetworkStats.Entry> android.os.Parcelable {
ctor public NetworkStats(long, int);
method @NonNull public android.net.NetworkStats add(@NonNull android.net.NetworkStats);
method @NonNull public android.net.NetworkStats addEntry(@NonNull android.net.NetworkStats.Entry);
method public int describeContents();
+ method @NonNull public java.util.Iterator<android.net.NetworkStats.Entry> iterator();
method @NonNull public android.net.NetworkStats subtract(@NonNull android.net.NetworkStats);
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkStats> CREATOR;
@@ -8631,6 +8882,7 @@ package android.net.wifi.nl80211 {
method @Nullable public android.net.wifi.nl80211.DeviceWiphyCapabilities getDeviceWiphyCapabilities(@NonNull String);
method @NonNull public java.util.List<android.net.wifi.nl80211.NativeScanResult> getScanResults(@NonNull String, int);
method @Nullable public android.net.wifi.nl80211.WifiNl80211Manager.TxPacketCounters getTxPacketCounters(@NonNull String);
+ method public boolean notifyCountryCodeChanged();
method @Nullable public static android.net.wifi.nl80211.WifiNl80211Manager.OemSecurityType parseOemSecurityTypeElement(int, int, @NonNull byte[]);
method @Deprecated public boolean registerApCallback(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.nl80211.WifiNl80211Manager.SoftApCallback);
method public boolean registerCountryCodeChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.nl80211.WifiNl80211Manager.CountryCodeChangedListener);
@@ -9628,12 +9880,17 @@ package android.permission {
public final class PermissionControllerManager {
method @RequiresPermission(anyOf={android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, android.Manifest.permission.RESTORE_RUNTIME_PERMISSIONS}) public void applyStagedRuntimePermissionBackup(@NonNull String, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_APP_HIBERNATION) public void getHibernationEligibility(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.IntConsumer);
method @RequiresPermission(android.Manifest.permission.GET_RUNTIME_PERMISSIONS) public void getRuntimePermissionBackup(@NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<byte[]>);
method public void getUnusedAppCount(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.IntConsumer);
method @RequiresPermission(android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS) public void revokeRuntimePermissions(@NonNull java.util.Map<java.lang.String,java.util.List<java.lang.String>>, boolean, int, @NonNull java.util.concurrent.Executor, @NonNull android.permission.PermissionControllerManager.OnRevokeRuntimePermissionsCallback);
method @RequiresPermission(anyOf={android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, android.Manifest.permission.RESTORE_RUNTIME_PERMISSIONS}) public void stageAndApplyRuntimePermissionsBackup(@NonNull byte[], @NonNull android.os.UserHandle);
field public static final int COUNT_ONLY_WHEN_GRANTED = 1; // 0x1
field public static final int COUNT_WHEN_SYSTEM = 2; // 0x2
+ field public static final int HIBERNATION_ELIGIBILITY_ELIGIBLE = 0; // 0x0
+ field public static final int HIBERNATION_ELIGIBILITY_EXEMPT_BY_SYSTEM = 1; // 0x1
+ field public static final int HIBERNATION_ELIGIBILITY_EXEMPT_BY_USER = 2; // 0x2
+ field public static final int HIBERNATION_ELIGIBILITY_UNKNOWN = -1; // 0xffffffff
field public static final int REASON_INSTALLER_POLICY_VIOLATION = 2; // 0x2
field public static final int REASON_MALWARE = 1; // 0x1
}
@@ -9651,6 +9908,7 @@ package android.permission {
method @BinderThread public abstract void onCountPermissionApps(@NonNull java.util.List<java.lang.String>, int, @NonNull java.util.function.IntConsumer);
method @BinderThread public abstract void onGetAppPermissions(@NonNull String, @NonNull java.util.function.Consumer<java.util.List<android.permission.RuntimePermissionPresentationInfo>>);
method @BinderThread public void onGetGroupOfPlatformPermission(@NonNull String, @NonNull java.util.function.Consumer<java.lang.String>);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_APP_HIBERNATION) public void onGetHibernationEligibility(@NonNull String, @NonNull java.util.function.IntConsumer);
method @BinderThread public abstract void onGetPermissionUsages(boolean, long, @NonNull java.util.function.Consumer<java.util.List<android.permission.RuntimePermissionUsageInfo>>);
method @BinderThread public void onGetPlatformPermissionsForGroup(@NonNull String, @NonNull java.util.function.Consumer<java.util.List<java.lang.String>>);
method @BinderThread public abstract void onGetRuntimePermissionsBackup(@NonNull android.os.UserHandle, @NonNull java.io.OutputStream, @NonNull Runnable);
@@ -9659,9 +9917,9 @@ package android.permission {
method @BinderThread public void onOneTimePermissionSessionTimeout(@NonNull String);
method @Deprecated @BinderThread public void onRestoreDelayedRuntimePermissionsBackup(@NonNull String, @NonNull android.os.UserHandle, @NonNull java.util.function.Consumer<java.lang.Boolean>);
method @Deprecated @BinderThread public void onRestoreRuntimePermissionsBackup(@NonNull android.os.UserHandle, @NonNull java.io.InputStream, @NonNull Runnable);
+ method @BinderThread public void onRevokeOwnPermissionsOnKill(@NonNull String, @NonNull java.util.List<java.lang.String>, @NonNull Runnable);
method @BinderThread public abstract void onRevokeRuntimePermission(@NonNull String, @NonNull String, @NonNull Runnable);
method @BinderThread public abstract void onRevokeRuntimePermissions(@NonNull java.util.Map<java.lang.String,java.util.List<java.lang.String>>, boolean, int, @NonNull String, @NonNull java.util.function.Consumer<java.util.Map<java.lang.String,java.util.List<java.lang.String>>>);
- method @BinderThread public void onSelfRevokePermissions(@NonNull String, @NonNull java.util.List<java.lang.String>, @NonNull Runnable);
method @Deprecated @BinderThread public abstract void onSetRuntimePermissionGrantStateByDeviceAdmin(@NonNull String, @NonNull String, @NonNull String, int, @NonNull java.util.function.Consumer<java.lang.Boolean>);
method @BinderThread public void onSetRuntimePermissionGrantStateByDeviceAdmin(@NonNull String, @NonNull android.permission.AdminPermissionControlParams, @NonNull java.util.function.Consumer<java.lang.Boolean>);
method @BinderThread public void onStageAndApplyRuntimePermissionsBackup(@NonNull android.os.UserHandle, @NonNull java.io.InputStream, @NonNull Runnable);
@@ -9869,6 +10127,7 @@ package android.provider {
method @RequiresPermission(android.Manifest.permission.WRITE_DEVICE_CONFIG) public static boolean setProperty(@NonNull String, @NonNull String, @Nullable String, boolean);
field public static final String NAMESPACE_ACTIVITY_MANAGER = "activity_manager";
field public static final String NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT = "activity_manager_native_boot";
+ field public static final String NAMESPACE_AMBIENT_CONTEXT_MANAGER_SERVICE = "ambient_context_manager_service";
field public static final String NAMESPACE_APPSEARCH = "appsearch";
field public static final String NAMESPACE_APP_COMPAT = "app_compat";
field public static final String NAMESPACE_APP_HIBERNATION = "app_hibernation";
@@ -9911,6 +10170,7 @@ 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_SUPPLEMENTAL_API = "supplemental_api";
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";
@@ -10139,6 +10399,7 @@ package android.provider {
field public static final String AUTO_REVOKE_DISABLED = "auto_revoke_disabled";
field public static final String COMPLETED_CATEGORY_PREFIX = "suggested.completed_category.";
field public static final String DOZE_ALWAYS_ON = "doze_always_on";
+ field public static final String FAST_PAIR_SCAN_ENABLED = "fast_pair_scan_enabled";
field public static final String HUSH_GESTURE_USED = "hush_gesture_used";
field public static final String INSTANT_APPS_ENABLED = "instant_apps_enabled";
field public static final String LAST_SETUP_SHOWN = "last_setup_shown";
@@ -10439,6 +10700,18 @@ package android.security.keystore.recovery {
}
+package android.service.ambientcontext {
+
+ public abstract class AmbientContextDetectionService extends android.app.Service {
+ ctor public AmbientContextDetectionService();
+ method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent);
+ method public abstract void onStartDetection(@NonNull android.app.ambientcontext.AmbientContextEventRequest, @NonNull String, @NonNull java.util.function.Consumer<android.app.ambientcontext.AmbientContextEventResponse>);
+ method public abstract void onStopDetection(@NonNull String);
+ field public static final String SERVICE_INTERFACE = "android.service.ambientcontext.AmbientContextDetectionService";
+ }
+
+}
+
package android.service.appprediction {
public abstract class AppPredictionService extends android.app.Service {
@@ -10890,6 +11163,15 @@ package android.service.games {
ctor public GameSession();
method public void onCreate();
method public void onDestroy();
+ method public void onGameTaskFocusChanged(boolean);
+ method public void setTaskOverlayView(@NonNull android.view.View, @NonNull android.view.ViewGroup.LayoutParams);
+ method public void takeScreenshot(@NonNull java.util.concurrent.Executor, @NonNull android.service.games.GameSession.ScreenshotCallback);
+ }
+
+ public static interface GameSession.ScreenshotCallback {
+ method public void onFailure(int);
+ method public void onSuccess(@NonNull android.graphics.Bitmap);
+ field public static final int ERROR_TAKE_SCREENSHOT_INTERNAL_ERROR = 0; // 0x0
}
public abstract class GameSessionService extends android.app.Service {
@@ -11030,6 +11312,7 @@ package android.service.persistentdata {
method @android.service.persistentdata.PersistentDataBlockManager.FlashLockState @RequiresPermission(anyOf={android.Manifest.permission.READ_OEM_UNLOCK_STATE, "android.permission.OEM_UNLOCK_STATE"}) public int getFlashLockState();
method public long getMaximumDataBlockSize();
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_OEM_UNLOCK_STATE, "android.permission.OEM_UNLOCK_STATE"}) public boolean getOemUnlockEnabled();
+ method @NonNull public String getPersistentDataPackageName();
method public byte[] read();
method @Deprecated @RequiresPermission("android.permission.OEM_UNLOCK_STATE") public void setOemUnlockEnabled(boolean);
method @RequiresPermission("android.permission.OEM_UNLOCK_STATE") public void wipe();
@@ -12987,6 +13270,7 @@ package android.telephony {
field public static final int ALLOWED_NETWORK_TYPES_REASON_USER = 0; // 0x0
field public static final int CALL_WAITING_STATUS_DISABLED = 2; // 0x2
field public static final int CALL_WAITING_STATUS_ENABLED = 1; // 0x1
+ field public static final int CALL_WAITING_STATUS_FDN_CHECK_FAILURE = 5; // 0x5
field public static final int CALL_WAITING_STATUS_NOT_SUPPORTED = 4; // 0x4
field public static final int CALL_WAITING_STATUS_UNKNOWN_ERROR = 3; // 0x3
field public static final String CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE = "CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE";
@@ -14312,18 +14596,16 @@ package android.telephony.ims {
public class ProvisioningManager {
method @NonNull public static android.telephony.ims.ProvisioningManager createForSubscriptionId(int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public int getProvisioningIntValue(int);
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public boolean getProvisioningStatusForCapability(int, int);
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public String getProvisioningStringValue(int);
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public boolean getRcsProvisioningStatusForCapability(int);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public boolean getRcsProvisioningStatusForCapability(int);
method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION}) public boolean isRcsVolteSingleRegistrationCapable() throws android.telephony.ims.ImsException;
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyRcsAutoConfigurationReceived(@NonNull byte[], boolean);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerProvisioningChangedCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ProvisioningManager.Callback) throws android.telephony.ims.ImsException;
method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION}) public void registerRcsProvisioningCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ProvisioningManager.RcsProvisioningCallback) throws android.telephony.ims.ImsException;
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningIntValue(int, int);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setProvisioningStatusForCapability(int, int, boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningStringValue(int, @NonNull String);
method @RequiresPermission(android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION) public void setRcsClientConfiguration(@NonNull android.telephony.ims.RcsClientConfiguration) throws android.telephony.ims.ImsException;
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setRcsProvisioningStatusForCapability(int, boolean);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setRcsProvisioningStatusForCapability(int, boolean);
method @RequiresPermission(android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION) public void triggerRcsReconfiguration();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterProvisioningChangedCallback(@NonNull android.telephony.ims.ProvisioningManager.Callback);
method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION}) public void unregisterRcsProvisioningCallback(@NonNull android.telephony.ims.ProvisioningManager.RcsProvisioningCallback);
@@ -14756,9 +15038,6 @@ package android.telephony.ims.feature {
method public void addCapabilities(int);
method public boolean isCapable(int);
method public void removeCapabilities(int);
- field public static final int CAPABILITY_TYPE_NONE = 0; // 0x0
- field public static final int CAPABILITY_TYPE_OPTIONS_UCE = 1; // 0x1
- field public static final int CAPABILITY_TYPE_PRESENCE_UCE = 2; // 0x2
}
}
@@ -14908,11 +15187,6 @@ package android.telephony.ims.stub {
method public void triggerFullNetworkRegistration(@IntRange(from=100, to=699) int, @Nullable String);
method public void triggerSipDelegateDeregistration();
method public void updateSipDelegateRegistration();
- field public static final int REGISTRATION_TECH_CROSS_SIM = 2; // 0x2
- field public static final int REGISTRATION_TECH_IWLAN = 1; // 0x1
- field public static final int REGISTRATION_TECH_LTE = 0; // 0x0
- field public static final int REGISTRATION_TECH_NONE = -1; // 0xffffffff
- field public static final int REGISTRATION_TECH_NR = 3; // 0x3
}
public class ImsSmsImplBase {
@@ -15190,6 +15464,8 @@ package android.view {
public interface WindowManager extends android.view.ViewManager {
method @RequiresPermission(android.Manifest.permission.RESTRICTED_VR_ACCESS) public android.graphics.Region getCurrentImeTouchRegion();
+ method public default void registerTaskFpsCallback(@IntRange(from=0) int, @NonNull android.window.TaskFpsCallback);
+ method public default void unregisterTaskFpsCallback(@NonNull android.window.TaskFpsCallback);
}
public static class WindowManager.LayoutParams extends android.view.ViewGroup.LayoutParams implements android.os.Parcelable {
@@ -15777,3 +16053,15 @@ package android.webkit {
}
+package android.window {
+
+ public final class TaskFpsCallback {
+ ctor public TaskFpsCallback(@NonNull java.util.concurrent.Executor, @NonNull android.window.TaskFpsCallback.OnFpsCallbackListener);
+ }
+
+ public static interface TaskFpsCallback.OnFpsCallbackListener {
+ method public void onFpsReported(float);
+ }
+
+}
+
diff --git a/core/api/system-lint-baseline.txt b/core/api/system-lint-baseline.txt
index 716f43e1d68a..3d756bafa292 100644
--- a/core/api/system-lint-baseline.txt
+++ b/core/api/system-lint-baseline.txt
@@ -91,6 +91,10 @@ MissingNullability: android.telephony.mbms.DownloadRequest.Builder#setServiceId(
+NoSettingsProvider: android.provider.Settings.Secure#FAST_PAIR_SCAN_ENABLED:
+ New setting keys are not allowed (Field: FAST_PAIR_SCAN_ENABLED); use getters/setters in relevant manager class
+
+
OnNameExpected: android.service.smartspace.SmartspaceService#notifySmartspaceEvent(android.app.smartspace.SmartspaceSessionId, android.app.smartspace.SmartspaceTargetEvent):
Methods implemented by developers should follow the on<Something> style, was `notifySmartspaceEvent`
@@ -103,6 +107,16 @@ ProtectedMember: android.service.notification.NotificationAssistantService#attac
+RethrowRemoteException: android.app.WallpaperManager#getWallpaperDimAmount():
+ Methods calling system APIs should rethrow `RemoteException` as `RuntimeException` (but do not list it in the throws clause)
+RethrowRemoteException: android.app.WallpaperManager#getWallpaperDimmingAmount():
+ Methods calling system APIs should rethrow `RemoteException` as `RuntimeException` (but do not list it in the throws clause)
+RethrowRemoteException: android.app.WallpaperManager#setWallpaperDimAmount(float):
+ Methods calling system APIs should rethrow `RemoteException` as `RuntimeException` (but do not list it in the throws clause)
+RethrowRemoteException: android.app.WallpaperManager#setWallpaperDimmingAmount(float):
+ Methods calling system APIs should rethrow `RemoteException` as `RuntimeException` (but do not list it in the throws clause)
+
+
SamShouldBeLast: android.accounts.AccountManager#addAccount(String, String, String[], android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler):
SamShouldBeLast: android.accounts.AccountManager#addOnAccountsUpdatedListener(android.accounts.OnAccountsUpdateListener, android.os.Handler, boolean):
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index d6a9f7faea65..ff26ae8d09fc 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -38,6 +38,7 @@ package android {
field public static final String RESET_APP_ERRORS = "android.permission.RESET_APP_ERRORS";
field public static final String REVOKE_POST_NOTIFICATIONS_WITHOUT_KILL = "android.permission.REVOKE_POST_NOTIFICATIONS_WITHOUT_KILL";
field public static final String SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS = "android.permission.SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS";
+ field public static final String SET_KEYBOARD_LAYOUT = "android.permission.SET_KEYBOARD_LAYOUT";
field public static final String START_TASKS_FROM_RECENTS = "android.permission.START_TASKS_FROM_RECENTS";
field public static final String SUSPEND_APPS = "android.permission.SUSPEND_APPS";
field public static final String TEST_BIOMETRIC = "android.permission.TEST_BIOMETRIC";
@@ -101,7 +102,7 @@ package android.animation {
package android.app {
- @UiContext public class Activity extends android.view.ContextThemeWrapper implements android.content.ComponentCallbacks2 android.view.KeyEvent.Callback android.view.LayoutInflater.Factory2 android.view.View.OnCreateContextMenuListener android.view.Window.Callback {
+ @UiContext public class Activity extends android.view.ContextThemeWrapper implements android.content.ComponentCallbacks2 android.view.KeyEvent.Callback android.view.LayoutInflater.Factory2 android.view.OnBackInvokedDispatcherOwner android.view.View.OnCreateContextMenuListener android.view.Window.Callback {
method public void onMovedToDisplay(int, android.content.res.Configuration);
}
@@ -1180,9 +1181,23 @@ package android.hardware.hdmi {
package android.hardware.input {
+ public final class InputDeviceIdentifier implements android.os.Parcelable {
+ ctor public InputDeviceIdentifier(@NonNull String, int, int);
+ method public int describeContents();
+ method @NonNull public String getDescriptor();
+ method public int getProductId();
+ method public int getVendorId();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.hardware.input.InputDeviceIdentifier> CREATOR;
+ }
+
public final class InputManager {
method public int getBlockUntrustedTouchesMode(@NonNull android.content.Context);
+ method @Nullable public String getCurrentKeyboardLayoutForInputDevice(@NonNull android.hardware.input.InputDeviceIdentifier);
+ method @NonNull public java.util.List<java.lang.String> getKeyboardLayoutDescriptorsForInputDevice(@NonNull android.view.InputDevice);
+ method @RequiresPermission(android.Manifest.permission.SET_KEYBOARD_LAYOUT) public void removeKeyboardLayoutForInputDevice(@NonNull android.hardware.input.InputDeviceIdentifier, @NonNull String);
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setBlockUntrustedTouchesMode(@NonNull android.content.Context, int);
+ method @RequiresPermission(android.Manifest.permission.SET_KEYBOARD_LAYOUT) public void setCurrentKeyboardLayoutForInputDevice(@NonNull android.hardware.input.InputDeviceIdentifier, @NonNull String);
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setMaximumObscuringOpacityForTouch(@FloatRange(from=0, to=1) float);
field public static final long BLOCK_UNTRUSTED_TOUCHES = 158002302L; // 0x96aec7eL
}
@@ -1928,6 +1943,9 @@ package android.os.storage {
method @NonNull public static String convert(@NonNull java.util.UUID);
method public boolean isAppIoBlocked(@NonNull java.util.UUID, int, int, int);
method public static boolean isUserKeyUnlocked(int);
+ field public static final String CACHE_RESERVE_PERCENT_HIGH_KEY = "cache_reserve_percent_high";
+ field public static final String CACHE_RESERVE_PERCENT_LOW_KEY = "cache_reserve_percent_low";
+ field public static final String STORAGE_THRESHOLD_PERCENT_HIGH_KEY = "storage_threshold_percent_high";
}
public final class StorageVolume implements android.os.Parcelable {
@@ -2737,6 +2755,7 @@ package android.view {
public final class InputDevice implements android.os.Parcelable {
method @RequiresPermission("android.permission.DISABLE_INPUT_DEVICE") public void disable();
method @RequiresPermission("android.permission.DISABLE_INPUT_DEVICE") public void enable();
+ method @NonNull public android.hardware.input.InputDeviceIdentifier getIdentifier();
}
public class KeyEvent extends android.view.InputEvent implements android.os.Parcelable {
@@ -2779,7 +2798,7 @@ package android.view {
method public void setView(@NonNull android.view.View, @NonNull android.view.WindowManager.LayoutParams);
}
- @UiThread public class View implements android.view.accessibility.AccessibilityEventSource android.graphics.drawable.Drawable.Callback android.view.KeyEvent.Callback {
+ @UiThread public class View implements android.view.accessibility.AccessibilityEventSource android.graphics.drawable.Drawable.Callback android.view.KeyEvent.Callback android.view.OnBackInvokedDispatcherOwner {
method public android.view.View getTooltipView();
method public boolean isAutofilled();
method public static boolean isDefaultFocusHighlightEnabled();
diff --git a/core/java/Android.bp b/core/java/Android.bp
index c9cbef2c9e9a..897bab429a87 100644
--- a/core/java/Android.bp
+++ b/core/java/Android.bp
@@ -121,6 +121,11 @@ filegroup {
],
}
+filegroup {
+ name: "ILogcatManagerService_aidl",
+ srcs: ["android/os/logcat/ILogcatManagerService.aidl"],
+}
+
genrule {
name: "statslog-framework-java-gen",
tools: ["stats-log-api-gen"],
diff --git a/core/java/android/accessibilityservice/AccessibilityGestureEvent.java b/core/java/android/accessibilityservice/AccessibilityGestureEvent.java
index 3c9b23251191..8e01779c6fac 100644
--- a/core/java/android/accessibilityservice/AccessibilityGestureEvent.java
+++ b/core/java/android/accessibilityservice/AccessibilityGestureEvent.java
@@ -172,7 +172,7 @@ public final class AccessibilityGestureEvent implements Parcelable {
private AccessibilityGestureEvent(@NonNull Parcel parcel) {
mGestureId = parcel.readInt();
mDisplayId = parcel.readInt();
- ParceledListSlice<MotionEvent> slice = parcel.readParcelable(getClass().getClassLoader());
+ ParceledListSlice<MotionEvent> slice = parcel.readParcelable(getClass().getClassLoader(), android.content.pm.ParceledListSlice.class);
mMotionEvents = slice.getList();
}
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index 04c784ea1c17..1167d0b1034f 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -1094,8 +1094,8 @@ public class AccessibilityServiceInfo implements Parcelable {
mInteractiveUiTimeout = parcel.readInt();
flags = parcel.readInt();
crashed = parcel.readInt() != 0;
- mComponentName = parcel.readParcelable(this.getClass().getClassLoader());
- mResolveInfo = parcel.readParcelable(null);
+ mComponentName = parcel.readParcelable(this.getClass().getClassLoader(), android.content.ComponentName.class);
+ mResolveInfo = parcel.readParcelable(null, android.content.pm.ResolveInfo.class);
mSettingsActivityName = parcel.readString();
mCapabilities = parcel.readInt();
mSummaryResId = parcel.readInt();
diff --git a/core/java/android/accessibilityservice/TouchInteractionController.java b/core/java/android/accessibilityservice/TouchInteractionController.java
index a8ba1d33f3b4..bb2b8d45fd61 100644
--- a/core/java/android/accessibilityservice/TouchInteractionController.java
+++ b/core/java/android/accessibilityservice/TouchInteractionController.java
@@ -24,6 +24,8 @@ import android.util.ArrayMap;
import android.view.MotionEvent;
import android.view.accessibility.AccessibilityInteractionClient;
+import java.util.LinkedList;
+import java.util.Queue;
import java.util.concurrent.Executor;
/**
@@ -102,6 +104,11 @@ public final class TouchInteractionController {
private boolean mServiceDetectsGestures;
/** Map of callbacks to executors. Lazily created when adding the first callback. */
private ArrayMap<Callback, Executor> mCallbacks;
+ // A list of motion events that should be queued until a pending transition has taken place.
+ private Queue<MotionEvent> mQueuedMotionEvents = new LinkedList<>();
+ // Whether this controller is waiting for a state transition.
+ // Motion events will be queued and sent to listeners after the transition has taken place.
+ private boolean mStateChangeRequested = false;
// The current state of the display.
private int mState = STATE_CLEAR;
@@ -169,6 +176,14 @@ public final class TouchInteractionController {
* main thread.
*/
void onMotionEvent(MotionEvent event) {
+ if (mStateChangeRequested) {
+ mQueuedMotionEvents.add(event);
+ } else {
+ sendEventToAllListeners(event);
+ }
+ }
+
+ private void sendEventToAllListeners(MotionEvent event) {
final ArrayMap<Callback, Executor> entries;
synchronized (mLock) {
// callbacks may remove themselves. Perform a shallow copy to avoid concurrent
@@ -209,6 +224,10 @@ public final class TouchInteractionController {
callback.onStateChanged(state);
}
}
+ mStateChangeRequested = false;
+ while (mQueuedMotionEvents.size() > 0) {
+ sendEventToAllListeners(mQueuedMotionEvents.poll());
+ }
}
/**
@@ -253,6 +272,7 @@ public final class TouchInteractionController {
} catch (RemoteException re) {
throw new RuntimeException(re);
}
+ mStateChangeRequested = true;
}
}
@@ -281,6 +301,7 @@ public final class TouchInteractionController {
} catch (RemoteException re) {
throw new RuntimeException(re);
}
+ mStateChangeRequested = true;
}
}
@@ -302,6 +323,7 @@ public final class TouchInteractionController {
} catch (RemoteException re) {
throw new RuntimeException(re);
}
+ mStateChangeRequested = true;
}
}
diff --git a/core/java/android/accounts/CantAddAccountActivity.java b/core/java/android/accounts/CantAddAccountActivity.java
index f7f232e5c422..107efc3cce95 100644
--- a/core/java/android/accounts/CantAddAccountActivity.java
+++ b/core/java/android/accounts/CantAddAccountActivity.java
@@ -16,9 +16,13 @@
package android.accounts;
+import static android.app.admin.DevicePolicyResources.Strings.Core.CANT_ADD_ACCOUNT_MESSAGE;
+
import android.app.Activity;
+import android.app.admin.DevicePolicyManager;
import android.os.Bundle;
import android.view.View;
+import android.widget.TextView;
import com.android.internal.R;
@@ -33,6 +37,12 @@ public class CantAddAccountActivity extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.app_not_authorized);
+
+ TextView view = findViewById(R.id.description);
+ String text = getSystemService(DevicePolicyManager.class).getString(
+ CANT_ADD_ACCOUNT_MESSAGE,
+ () -> getString(R.string.error_message_change_not_allowed));
+ view.setText(text);
}
public void onCancelButtonClicked(View view) {
diff --git a/core/java/android/accounts/ChooseTypeAndAccountActivity.java b/core/java/android/accounts/ChooseTypeAndAccountActivity.java
index 2e9f73ca388e..0d82ac942148 100644
--- a/core/java/android/accounts/ChooseTypeAndAccountActivity.java
+++ b/core/java/android/accounts/ChooseTypeAndAccountActivity.java
@@ -15,7 +15,10 @@
*/
package android.accounts;
+import static android.app.admin.DevicePolicyResources.Strings.Core.CANT_ADD_ACCOUNT_MESSAGE;
+
import android.app.Activity;
+import android.app.admin.DevicePolicyManager;
import android.content.Intent;
import android.os.Bundle;
import android.os.Parcelable;
@@ -199,7 +202,14 @@ public class ChooseTypeAndAccountActivity extends Activity
if (mPossiblyVisibleAccounts.isEmpty() && mDisallowAddAccounts) {
requestWindowFeature(Window.FEATURE_NO_TITLE);
+
setContentView(R.layout.app_not_authorized);
+ TextView view = findViewById(R.id.description);
+ String text = getSystemService(DevicePolicyManager.class).getString(
+ CANT_ADD_ACCOUNT_MESSAGE,
+ () -> getString(R.string.error_message_change_not_allowed));
+ view.setText(text);
+
mDontShowPicker = true;
}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 283345f07337..a7b96a6f136d 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -111,6 +111,8 @@ import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
+import android.view.OnBackInvokedDispatcher;
+import android.view.OnBackInvokedDispatcherOwner;
import android.view.RemoteAnimationDefinition;
import android.view.SearchEvent;
import android.view.View;
@@ -736,7 +738,8 @@ public class Activity extends ContextThemeWrapper
Window.Callback, KeyEvent.Callback,
OnCreateContextMenuListener, ComponentCallbacks2,
Window.OnWindowDismissedCallback,
- ContentCaptureManager.ContentCaptureClient {
+ ContentCaptureManager.ContentCaptureClient,
+ OnBackInvokedDispatcherOwner {
private static final String TAG = "Activity";
private static final boolean DEBUG_LIFECYCLE = false;
@@ -1521,7 +1524,10 @@ public class Activity extends ContextThemeWrapper
}
private void dispatchActivityConfigurationChanged() {
- getApplication().dispatchActivityConfigurationChanged(this);
+ // In case the new config comes before mApplication is assigned.
+ if (getApplication() != null) {
+ getApplication().dispatchActivityConfigurationChanged(this);
+ }
Object[] callbacks = collectActivityLifecycleCallbacks();
if (callbacks != null) {
for (int i = 0; i < callbacks.length; i++) {
@@ -8672,4 +8678,22 @@ public class Activity extends ContextThemeWrapper
return (w != null && w.peekDecorView() != null);
}
}
+
+ /**
+ * Returns the {@link OnBackInvokedDispatcher} instance associated with the window that this
+ * activity is attached to.
+ *
+ * Returns null if the activity is not attached to a window with a decor.
+ */
+ @Nullable
+ @Override
+ public OnBackInvokedDispatcher getOnBackInvokedDispatcher() {
+ if (mWindow != null) {
+ View decorView = mWindow.getDecorView();
+ if (decorView != null) {
+ return decorView.getOnBackInvokedDispatcher();
+ }
+ }
+ return null;
+ }
}
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 9f8d24662c8d..a1409839ff63 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -1903,7 +1903,7 @@ public class ActivityManager {
public void readFromParcel(Parcel source) {
id = source.readInt();
persistentId = source.readInt();
- childrenTaskInfos = source.readArrayList(RecentTaskInfo.class.getClassLoader());
+ childrenTaskInfos = source.readArrayList(RecentTaskInfo.class.getClassLoader(), android.app.ActivityManager.RecentTaskInfo.class);
lastSnapshotData.taskSize = source.readTypedObject(Point.CREATOR);
lastSnapshotData.contentInsets = source.readTypedObject(Rect.CREATOR);
lastSnapshotData.bufferSize = source.readTypedObject(Point.CREATOR);
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 686ca3bad087..ea6271412289 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -61,6 +61,7 @@ import android.app.servertransaction.PendingTransactionActions.StopInfo;
import android.app.servertransaction.ResumeActivityItem;
import android.app.servertransaction.TransactionExecutor;
import android.app.servertransaction.TransactionExecutorHelper;
+import android.bluetooth.BluetoothFrameworkInitializer;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.AttributionSource;
import android.content.AutofillOptions;
@@ -105,9 +106,11 @@ import android.media.MediaFrameworkPlatformInitializer;
import android.media.MediaServiceManager;
import android.net.ConnectivityManager;
import android.net.Proxy;
+import android.net.TrafficStats;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Binder;
+import android.os.BluetoothServiceManager;
import android.os.Build;
import android.os.Bundle;
import android.os.CancellationSignal;
@@ -6725,6 +6728,13 @@ public final class ActivityThread extends ClientTransactionHandler
NetworkSecurityConfigProvider.install(appContext);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+ // For backward compatibility, TrafficStats needs static access to the application context.
+ // But for isolated apps which cannot access network related services, service discovery
+ // is restricted. Hence, calling this would result in NPE.
+ if (!Process.isIsolated()) {
+ TrafficStats.init(appContext);
+ }
+
// Continue loading instrumentation.
if (ii != null) {
initInstrumentation(ii, data, appContext);
@@ -7911,6 +7921,7 @@ public final class ActivityThread extends ClientTransactionHandler
StatsFrameworkInitializer.setStatsServiceManager(new StatsServiceManager());
MediaFrameworkPlatformInitializer.setMediaServiceManager(new MediaServiceManager());
MediaFrameworkInitializer.setMediaServiceManager(new MediaServiceManager());
+ BluetoothFrameworkInitializer.setBluetoothServiceManager(new BluetoothServiceManager());
}
private void purgePendingResources() {
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 565f69090c6b..68c69e555bda 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -4058,7 +4058,7 @@ public class AppOpsManager {
LongSparseArray<NoteOpEvent> array = new LongSparseArray<>(numEntries);
for (int i = 0; i < numEntries; i++) {
- array.put(source.readLong(), source.readParcelable(null));
+ array.put(source.readLong(), source.readParcelable(null, android.app.AppOpsManager.NoteOpEvent.class));
}
return array;
@@ -5178,7 +5178,7 @@ public class AppOpsManager {
final int[] uids = parcel.createIntArray();
if (!ArrayUtils.isEmpty(uids)) {
final ParceledListSlice<HistoricalUidOps> listSlice = parcel.readParcelable(
- HistoricalOps.class.getClassLoader());
+ HistoricalOps.class.getClassLoader(), android.content.pm.ParceledListSlice.class);
final List<HistoricalUidOps> uidOps = (listSlice != null)
? listSlice.getList() : null;
if (uidOps == null) {
@@ -10000,7 +10000,7 @@ public class AppOpsManager {
private static @Nullable List<AttributedOpEntry> readDiscreteAccessArrayFromParcel(
@NonNull Parcel parcel) {
- final ParceledListSlice<AttributedOpEntry> listSlice = parcel.readParcelable(null);
+ final ParceledListSlice<AttributedOpEntry> listSlice = parcel.readParcelable(null, android.content.pm.ParceledListSlice.class);
return listSlice == null ? null : listSlice.getList();
}
diff --git a/core/java/android/app/Application.java b/core/java/android/app/Application.java
index 7f849ef00d7d..9eb3e8fb0160 100644
--- a/core/java/android/app/Application.java
+++ b/core/java/android/app/Application.java
@@ -29,9 +29,13 @@ import android.content.Intent;
import android.content.res.Configuration;
import android.os.Build;
import android.os.Bundle;
+import android.util.ArrayMap;
import android.util.Log;
+import android.util.Slog;
import android.view.autofill.AutofillManager;
+import com.android.internal.annotations.GuardedBy;
+
import java.util.ArrayList;
/**
@@ -53,6 +57,10 @@ import java.util.ArrayList;
*/
public class Application extends ContextWrapper implements ComponentCallbacks2 {
private static final String TAG = "Application";
+
+ /** Whether to enable the check to detect "duplicate application instances". */
+ private static final boolean DEBUG_DUP_APP_INSTANCES = true;
+
@UnsupportedAppUsage
private ArrayList<ActivityLifecycleCallbacks> mActivityLifecycleCallbacks =
new ArrayList<ActivityLifecycleCallbacks>();
@@ -66,6 +74,13 @@ public class Application extends ContextWrapper implements ComponentCallbacks2 {
@UnsupportedAppUsage
public LoadedApk mLoadedApk;
+ @GuardedBy("sInstances")
+ private static final ArrayMap<Class<?>, Application> sInstances =
+ DEBUG_DUP_APP_INSTANCES ? new ArrayMap<>(1) : null;
+
+ // Only set when DEBUG_DUP_APP_INSTANCES is true.
+ private StackTrace mConstructorStackTrace;
+
public interface ActivityLifecycleCallbacks {
/**
@@ -231,6 +246,41 @@ public class Application extends ContextWrapper implements ComponentCallbacks2 {
public Application() {
super(null);
+ if (DEBUG_DUP_APP_INSTANCES) {
+ checkDuplicateInstances();
+ }
+ }
+
+ private void checkDuplicateInstances() {
+ final Class<?> myClass = this.getClass();
+
+ // We only activate this check for custom application classes.
+ // Otherwise, it'd misfire if multiple apps share the same process, if all of them use
+ // the same Application class (on the same classloader).
+ if (myClass == Application.class) {
+ return;
+ }
+ synchronized (sInstances) {
+ final Application firstInstance = sInstances.get(myClass);
+ if (firstInstance == null) {
+ this.mConstructorStackTrace = new StackTrace("First ctor was called here");
+ sInstances.put(myClass, this);
+ return;
+ }
+ final StackTrace currentStackTrace = new StackTrace("Current ctor was called here",
+ firstInstance.mConstructorStackTrace);
+ this.mConstructorStackTrace = currentStackTrace;
+ Slog.wtf(TAG, "Application ctor called twice for " + myClass
+ + " first LoadedApk=" + firstInstance.getLoadedApkInfo(),
+ currentStackTrace);
+ }
+ }
+
+ private String getLoadedApkInfo() {
+ if (mLoadedApk == null) {
+ return "null";
+ }
+ return mLoadedApk + "/pkg=" + mLoadedApk.mPackageName;
}
/**
diff --git a/core/java/android/app/AutomaticZenRule.java b/core/java/android/app/AutomaticZenRule.java
index 7a806bdf473d..c0aebeed596a 100644
--- a/core/java/android/app/AutomaticZenRule.java
+++ b/core/java/android/app/AutomaticZenRule.java
@@ -118,11 +118,11 @@ public final class AutomaticZenRule implements Parcelable {
name = source.readString();
}
interruptionFilter = source.readInt();
- conditionId = source.readParcelable(null);
- owner = source.readParcelable(null);
- configurationActivity = source.readParcelable(null);
+ conditionId = source.readParcelable(null, android.net.Uri.class);
+ owner = source.readParcelable(null, android.content.ComponentName.class);
+ configurationActivity = source.readParcelable(null, android.content.ComponentName.class);
creationTime = source.readLong();
- mZenPolicy = source.readParcelable(null);
+ mZenPolicy = source.readParcelable(null, android.service.notification.ZenPolicy.class);
mModified = source.readInt() == ENABLED;
mPkg = source.readString();
}
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index fa48730d4950..f3315a8dc089 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2179,8 +2179,8 @@ class ContextImpl extends Context {
}
@Override
- public void selfRevokePermissions(@NonNull Collection<String> permissions) {
- getSystemService(PermissionManager.class).selfRevokePermissions(permissions);
+ public void revokeOwnPermissionsOnKill(@NonNull Collection<String> permissions) {
+ getSystemService(PermissionManager.class).revokeOwnPermissionsOnKill(permissions);
}
@Override
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
index 306035341ea3..a7fb83bfcf5e 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -52,6 +52,8 @@ import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
+import android.view.OnBackInvokedDispatcher;
+import android.view.OnBackInvokedDispatcherOwner;
import android.view.SearchEvent;
import android.view.View;
import android.view.View.OnCreateContextMenuListener;
@@ -94,7 +96,8 @@ import java.lang.ref.WeakReference;
* </div>
*/
public class Dialog implements DialogInterface, Window.Callback,
- KeyEvent.Callback, OnCreateContextMenuListener, Window.OnWindowDismissedCallback {
+ KeyEvent.Callback, OnCreateContextMenuListener, Window.OnWindowDismissedCallback,
+ OnBackInvokedDispatcherOwner {
private static final String TAG = "Dialog";
@UnsupportedAppUsage
private Activity mOwnerActivity;
@@ -1439,4 +1442,22 @@ public class Dialog implements DialogInterface, Window.Callback,
}
}
}
+
+ /**
+ * Returns the {@link OnBackInvokedDispatcher} instance associated with the window that this
+ * dialog is attached to.
+ *
+ * Returns null if the dialog is not attached to a window with a decor.
+ */
+ @Nullable
+ @Override
+ public OnBackInvokedDispatcher getOnBackInvokedDispatcher() {
+ if (mWindow != null) {
+ View decorView = mWindow.getDecorView();
+ if (decorView != null) {
+ return decorView.getOnBackInvokedDispatcher();
+ }
+ }
+ return null;
+ }
}
diff --git a/core/java/android/app/GameManager.java b/core/java/android/app/GameManager.java
index 29e1b70097f2..76471d30eaf9 100644
--- a/core/java/android/app/GameManager.java
+++ b/core/java/android/app/GameManager.java
@@ -119,6 +119,31 @@ public final class GameManager {
}
/**
+ * Returns the {@link GameModeInfo} associated with the game associated with
+ * the given {@code packageName}. If the given package is not a game, {@code null} is
+ * always returned.
+ * <p>
+ * An application can use <code>android:isGame="true"</code> or
+ * <code>android:appCategory="game"</code> to indicate that the application is a game.
+ * If the manifest doesn't define a category, the category can also be
+ * provided by the installer via
+ * {@link android.content.pm.PackageManager#setApplicationCategoryHint(String, int)}.
+ * <p>
+ *
+ * @hide
+ */
+ @SystemApi
+ @UserHandleAware
+ @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE)
+ public @Nullable GameModeInfo getGameModeInfo(@NonNull String packageName) {
+ try {
+ return mService.getGameModeInfo(packageName, mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Sets the game mode for the given package.
* <p>
* The caller must have {@link android.Manifest.permission#MANAGE_GAME_MODE}.
diff --git a/core/java/android/app/GameModeInfo.aidl b/core/java/android/app/GameModeInfo.aidl
new file mode 100644
index 000000000000..3b13201c5d1b
--- /dev/null
+++ b/core/java/android/app/GameModeInfo.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+/**
+ * @hide
+ */
+parcelable GameModeInfo; \ No newline at end of file
diff --git a/core/java/android/app/GameModeInfo.java b/core/java/android/app/GameModeInfo.java
new file mode 100644
index 000000000000..fe0ac352404d
--- /dev/null
+++ b/core/java/android/app/GameModeInfo.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * GameModeInfo returned from {@link GameManager#getGameModeInfo(String)}.
+ * @hide
+ */
+@SystemApi
+public final class GameModeInfo implements Parcelable {
+
+ public static final @NonNull Creator<GameModeInfo> CREATOR = new Creator<GameModeInfo>() {
+ @Override
+ public GameModeInfo createFromParcel(Parcel in) {
+ return new GameModeInfo(in);
+ }
+
+ @Override
+ public GameModeInfo[] newArray(int size) {
+ return new GameModeInfo[size];
+ }
+ };
+
+ public GameModeInfo(@GameManager.GameMode int activeGameMode,
+ @NonNull @GameManager.GameMode int[] availableGameModes) {
+ mActiveGameMode = activeGameMode;
+ mAvailableGameModes = availableGameModes;
+ }
+
+ GameModeInfo(Parcel in) {
+ mActiveGameMode = in.readInt();
+ final int availableGameModesCount = in.readInt();
+ mAvailableGameModes = new int[availableGameModesCount];
+ in.readIntArray(mAvailableGameModes);
+ }
+
+ /**
+ * Returns the {@link GameManager.GameMode} the application is currently using.
+ * Developers can enable game modes by adding
+ * <code>
+ * <meta-data android:name="android.game_mode_intervention"
+ * android:resource="@xml/GAME_MODE_CONFIG_FILE" />
+ * </code>
+ * to the {@link <application> tag}, where the GAME_MODE_CONFIG_FILE is an XML file that
+ * specifies the game mode enablement and configuration:
+ * <code>
+ * <game-mode-config xmlns:android="http://schemas.android.com/apk/res/android"
+ * android:gameModePerformance="true"
+ * android:gameModeBattery="false"
+ * />
+ * </code>
+ */
+ public @GameManager.GameMode int getActiveGameMode() {
+ return mActiveGameMode;
+ }
+
+ /**
+ * The collection of {@link GameManager.GameMode GameModes} that can be applied to the game.
+ */
+ @NonNull
+ public @GameManager.GameMode int[] getAvailableGameModes() {
+ return mAvailableGameModes;
+ }
+
+ // Ideally there should be callback that the caller can register to know when the available
+ // GameMode and/or the active GameMode is changed, however, there's no concrete use case
+ // at the moment so there's no callback mechanism introduced .
+ private final @GameManager.GameMode int[] mAvailableGameModes;
+ private final @GameManager.GameMode int mActiveGameMode;
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mActiveGameMode);
+ dest.writeInt(mAvailableGameModes.length);
+ dest.writeIntArray(mAvailableGameModes);
+ }
+}
diff --git a/core/java/android/app/GrantedUriPermission.java b/core/java/android/app/GrantedUriPermission.java
index 48d5b8cc126b..a71cb4a11af8 100644
--- a/core/java/android/app/GrantedUriPermission.java
+++ b/core/java/android/app/GrantedUriPermission.java
@@ -68,7 +68,7 @@ public class GrantedUriPermission implements Parcelable {
};
private GrantedUriPermission(Parcel in) {
- uri = in.readParcelable(null);
+ uri = in.readParcelable(null, android.net.Uri.class);
packageName = in.readString();
}
}
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index a9ec11edd680..0801b2481f0c 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -72,6 +72,7 @@ import android.view.IRemoteAnimationRunner;
import android.view.RemoteAnimationDefinition;
import android.view.RemoteAnimationAdapter;
import android.window.IWindowOrganizerController;
+import android.window.BackNavigationInfo;
import android.window.SplashScreenView;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.os.IResultReceiver;
@@ -346,7 +347,8 @@ interface IActivityTaskManager {
void setRunningRemoteTransitionDelegate(in IApplicationThread caller);
/**
- * Prepare the back preview in the server
+ * Prepare the back navigation in the server. This setups the leashed for sysui to animate
+ * the back gesture and returns the data needed for the animation.
*/
- void startBackPreview(IRemoteAnimationRunner runner);
+ android.window.BackNavigationInfo startBackNavigation();
}
diff --git a/core/java/android/app/IGameManagerService.aidl b/core/java/android/app/IGameManagerService.aidl
index d9aa586c6bbb..57de8c70e742 100644
--- a/core/java/android/app/IGameManagerService.aidl
+++ b/core/java/android/app/IGameManagerService.aidl
@@ -16,6 +16,7 @@
package android.app;
+import android.app.GameModeInfo;
import android.app.GameState;
/**
@@ -27,4 +28,5 @@ interface IGameManagerService {
int[] getAvailableGameModes(String packageName);
boolean getAngleEnabled(String packageName, int userId);
void setGameState(String packageName, in GameState gameState, int userId);
-} \ No newline at end of file
+ GameModeInfo getGameModeInfo(String packageName, int userId);
+}
diff --git a/core/java/android/app/IUiModeManager.aidl b/core/java/android/app/IUiModeManager.aidl
index 440dd629c114..55afed20c826 100644
--- a/core/java/android/app/IUiModeManager.aidl
+++ b/core/java/android/app/IUiModeManager.aidl
@@ -48,20 +48,43 @@ interface IUiModeManager {
/**
* Sets the night mode.
+ * <p>
* The mode can be one of:
- * 1 - notnight mode
- * 2 - night mode
- * 3 - automatic mode switching
+ * <ol>notnight mode</ol>
+ * <ol>night mode</ol>
+ * <ol>custom schedule mode switching</ol>
*/
void setNightMode(int mode);
/**
- * Gets the currently configured night mode. Return 1 for notnight,
- * 2 for night, and 3 for automatic mode switching.
+ * Gets the currently configured night mode.
+ * <p>
+ * Returns
+ * <ol>notnight mode</ol>
+ * <ol>night mode</ol>
+ * <ol>custom schedule mode switching</ol>
*/
int getNightMode();
/**
+ * Sets the current night mode to {@link #MODE_NIGHT_CUSTOM} with the custom night mode type
+ * {@code nightModeCustomType}.
+ *
+ * @param nightModeCustomType
+ * @hide
+ */
+ void setNightModeCustomType(int nightModeCustomType);
+
+ /**
+ * Returns the custom night mode type.
+ * <p>
+ * If the current night mode is not {@link #MODE_NIGHT_CUSTOM}, returns
+ * {@link #MODE_NIGHT_CUSTOM_TYPE_UNKNOWN}.
+ * @hide
+ */
+ int getNightModeCustomType();
+
+ /**
* Sets the dark mode for the given application. This setting is persisted and will override the
* system configuration for this application.
* 1 - notnight mode
@@ -81,8 +104,21 @@ interface IUiModeManager {
boolean isNightModeLocked();
/**
- * [De]Activates night mode
- */
+ * [De]activating night mode for the current user if the current night mode is custom and the
+ * custom type matches {@code nightModeCustomType}.
+ *
+ * @param nightModeCustomType the specify type of custom mode
+ * @param active {@code true} to activate night mode. Otherwise, deactivate night mode
+ * @return {@code true} if night mode has successfully activated for the requested
+ * {@code nightModeCustomType}.
+ * @hide
+ */
+ boolean setNightModeActivatedForCustomMode(int nightModeCustom, boolean active);
+
+ /**
+ * [De]Activates night mode.
+ * @hide
+ */
boolean setNightModeActivated(boolean active);
/**
diff --git a/core/java/android/app/IWallpaperManager.aidl b/core/java/android/app/IWallpaperManager.aidl
index 4f7c6841d6bb..28c273ec50a6 100644
--- a/core/java/android/app/IWallpaperManager.aidl
+++ b/core/java/android/app/IWallpaperManager.aidl
@@ -204,4 +204,27 @@ interface IWallpaperManager {
* @hide
*/
void notifyGoingToSleep(int x, int y, in Bundle extras);
+
+ /**
+ * Sets the wallpaper dim amount between [0f, 1f] which would be blended with the system default
+ * dimming. 0f doesn't add any additional dimming and 1f makes the wallpaper fully black.
+ *
+ * @hide
+ */
+ oneway void setWallpaperDimAmount(float dimAmount);
+
+ /**
+ * Gets the current additional dim amount set on the wallpaper. 0f means no application has
+ * added any dimming on top of the system default dim amount.
+ *
+ * @hide
+ */
+ float getWallpaperDimAmount();
+
+ /**
+ * Whether the lock screen wallpaper is different from the system wallpaper.
+ *
+ * @hide
+ */
+ boolean lockScreenWallpaperExists();
}
diff --git a/core/java/android/app/NotificationChannelGroup.java b/core/java/android/app/NotificationChannelGroup.java
index cd6df0b231d9..f97415ca20c8 100644
--- a/core/java/android/app/NotificationChannelGroup.java
+++ b/core/java/android/app/NotificationChannelGroup.java
@@ -100,7 +100,7 @@ public final class NotificationChannelGroup implements Parcelable {
} else {
mDescription = null;
}
- in.readParcelableList(mChannels, NotificationChannel.class.getClassLoader());
+ in.readParcelableList(mChannels, NotificationChannel.class.getClassLoader(), android.app.NotificationChannel.class);
mBlocked = in.readBoolean();
mUserLockedFields = in.readInt();
}
diff --git a/core/java/android/app/RemoteInputHistoryItem.java b/core/java/android/app/RemoteInputHistoryItem.java
index 091db3f142ae..32f89819fb1f 100644
--- a/core/java/android/app/RemoteInputHistoryItem.java
+++ b/core/java/android/app/RemoteInputHistoryItem.java
@@ -48,7 +48,7 @@ public class RemoteInputHistoryItem implements Parcelable {
protected RemoteInputHistoryItem(Parcel in) {
mText = in.readCharSequence();
mMimeType = in.readStringNoHelper();
- mUri = in.readParcelable(Uri.class.getClassLoader());
+ mUri = in.readParcelable(Uri.class.getClassLoader(), android.net.Uri.class);
}
public static final Creator<RemoteInputHistoryItem> CREATOR =
diff --git a/core/java/android/app/ServiceStartNotAllowedException.java b/core/java/android/app/ServiceStartNotAllowedException.java
index 33285b2190eb..b1f47eee4bfd 100644
--- a/core/java/android/app/ServiceStartNotAllowedException.java
+++ b/core/java/android/app/ServiceStartNotAllowedException.java
@@ -40,4 +40,11 @@ public abstract class ServiceStartNotAllowedException extends IllegalStateExcept
return new BackgroundServiceStartNotAllowedException(message);
}
}
+
+ @Override
+ public synchronized Throwable getCause() {
+ // "Cause" is often used for clustering exceptions, and developers don't want to have it
+ // for this exception. b/210890426
+ return null;
+ }
}
diff --git a/core/java/android/app/StackTrace.java b/core/java/android/app/StackTrace.java
index ec058f88118b..6a77abdefea9 100644
--- a/core/java/android/app/StackTrace.java
+++ b/core/java/android/app/StackTrace.java
@@ -24,4 +24,8 @@ public class StackTrace extends Exception {
public StackTrace(String message) {
super(message);
}
+
+ public StackTrace(String message, Throwable innerStackTrace) {
+ super(message, innerStackTrace);
+ }
}
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 67c42f69d079..7f8e46edf594 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -24,6 +24,8 @@ import android.annotation.SystemApi;
import android.app.ContextImpl.ServiceInitializationState;
import android.app.admin.DevicePolicyManager;
import android.app.admin.IDevicePolicyManager;
+import android.app.ambientcontext.AmbientContextManager;
+import android.app.ambientcontext.IAmbientContextEventObserver;
import android.app.appsearch.AppSearchManagerFrameworkInitializer;
import android.app.blob.BlobStoreManagerFrameworkInitializer;
import android.app.contentsuggestions.ContentSuggestionsManager;
@@ -49,7 +51,7 @@ import android.app.usage.StorageStatsManager;
import android.app.usage.UsageStatsManager;
import android.apphibernation.AppHibernationManager;
import android.appwidget.AppWidgetManager;
-import android.bluetooth.BluetoothManager;
+import android.bluetooth.BluetoothFrameworkInitializer;
import android.companion.CompanionDeviceManager;
import android.companion.ICompanionDeviceManager;
import android.companion.virtual.IVirtualDeviceManager;
@@ -124,8 +126,8 @@ import android.media.projection.MediaProjectionManager;
import android.media.soundtrigger.SoundTriggerManager;
import android.media.tv.ITvInputManager;
import android.media.tv.TvInputManager;
-import android.media.tv.interactive.ITvIAppManager;
-import android.media.tv.interactive.TvIAppManager;
+import android.media.tv.interactive.ITvInteractiveAppManager;
+import android.media.tv.interactive.TvInteractiveAppManager;
import android.media.tv.tunerresourcemanager.ITunerResourceManager;
import android.media.tv.tunerresourcemanager.TunerResourceManager;
import android.nearby.NearbyFrameworkInitializer;
@@ -346,13 +348,6 @@ public final class SystemServiceRegistry {
return new MediaRouter(ctx);
}});
- registerService(Context.BLUETOOTH_SERVICE, BluetoothManager.class,
- new CachedServiceFetcher<BluetoothManager>() {
- @Override
- public BluetoothManager createService(ContextImpl ctx) {
- return new BluetoothManager(ctx);
- }});
-
registerService(Context.HDMI_CONTROL_SERVICE, HdmiControlManager.class,
new StaticServiceFetcher<HdmiControlManager>() {
@Override
@@ -964,13 +959,16 @@ public final class SystemServiceRegistry {
}
});
- registerService(Context.TV_IAPP_SERVICE, TvIAppManager.class,
- new CachedServiceFetcher<TvIAppManager>() {
+ registerService(Context.TV_INTERACTIVE_APP_SERVICE, TvInteractiveAppManager.class,
+ new CachedServiceFetcher<TvInteractiveAppManager>() {
@Override
- public TvIAppManager createService(ContextImpl ctx) throws ServiceNotFoundException {
- IBinder iBinder = ServiceManager.getServiceOrThrow(Context.TV_IAPP_SERVICE);
- ITvIAppManager service = ITvIAppManager.Stub.asInterface(iBinder);
- return new TvIAppManager(service, ctx.getUserId());
+ public TvInteractiveAppManager createService(ContextImpl ctx)
+ throws ServiceNotFoundException {
+ IBinder iBinder =
+ ServiceManager.getServiceOrThrow(Context.TV_INTERACTIVE_APP_SERVICE);
+ ITvInteractiveAppManager service =
+ ITvInteractiveAppManager.Stub.asInterface(iBinder);
+ return new TvInteractiveAppManager(service, ctx.getUserId());
}});
registerService(Context.TV_INPUT_SERVICE, TvInputManager.class,
@@ -1021,19 +1019,21 @@ public final class SystemServiceRegistry {
}});
registerService(Context.PERSISTENT_DATA_BLOCK_SERVICE, PersistentDataBlockManager.class,
- new StaticServiceFetcher<PersistentDataBlockManager>() {
+ new CachedServiceFetcher<PersistentDataBlockManager>() {
@Override
- public PersistentDataBlockManager createService() throws ServiceNotFoundException {
+ public PersistentDataBlockManager createService(ContextImpl ctx)
+ throws ServiceNotFoundException {
IBinder b = ServiceManager.getServiceOrThrow(Context.PERSISTENT_DATA_BLOCK_SERVICE);
IPersistentDataBlockService persistentDataBlockService =
IPersistentDataBlockService.Stub.asInterface(b);
if (persistentDataBlockService != null) {
- return new PersistentDataBlockManager(persistentDataBlockService);
+ return new PersistentDataBlockManager(ctx, persistentDataBlockService);
} else {
// not supported
return null;
}
- }});
+ }
+ });
registerService(Context.OEM_LOCK_SERVICE, OemLockManager.class,
new StaticServiceFetcher<OemLockManager>() {
@@ -1518,6 +1518,18 @@ public final class SystemServiceRegistry {
}
});
+ registerService(Context.AMBIENT_CONTEXT_SERVICE, AmbientContextManager.class,
+ new CachedServiceFetcher<AmbientContextManager>() {
+ @Override
+ public AmbientContextManager createService(ContextImpl ctx)
+ throws ServiceNotFoundException {
+ IBinder iBinder = ServiceManager.getServiceOrThrow(
+ Context.AMBIENT_CONTEXT_SERVICE);
+ IAmbientContextEventObserver manager =
+ IAmbientContextEventObserver.Stub.asInterface(iBinder);
+ return new AmbientContextManager(ctx.getOuterContext(), manager);
+ }});
+
sInitializing = true;
try {
// Note: the following functions need to be @SystemApis, once they become mainline
@@ -1525,6 +1537,7 @@ public final class SystemServiceRegistry {
ConnectivityFrameworkInitializer.registerServiceWrappers();
JobSchedulerFrameworkInitializer.registerServiceWrappers();
BlobStoreManagerFrameworkInitializer.initialize();
+ BluetoothFrameworkInitializer.registerServiceWrappers();
TelephonyFrameworkInitializer.registerServiceWrappers();
AppSearchManagerFrameworkInitializer.initialize();
WifiFrameworkInitializer.registerServiceWrappers();
diff --git a/core/java/android/app/UiModeManager.java b/core/java/android/app/UiModeManager.java
index 973a8fb068d1..73a9e5a221c7 100644
--- a/core/java/android/app/UiModeManager.java
+++ b/core/java/android/app/UiModeManager.java
@@ -243,6 +243,45 @@ public class UiModeManager {
*/
public static final int MODE_NIGHT_YES = 2;
+ /**
+ * Granular types for {@link MODE_NIGHT_CUSTOM_TYPE_BEDTIME}
+ * @hide
+ */
+ @IntDef(prefix = { "MODE_NIGHT_CUSTOM_TYPE_" }, value = {
+ MODE_NIGHT_CUSTOM_TYPE_UNKNOWN,
+ MODE_NIGHT_CUSTOM_TYPE_SCHEDULE,
+ MODE_NIGHT_CUSTOM_TYPE_BEDTIME,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface NightModeCustomType {}
+
+ /**
+ * A granular type for {@link #MODE_NIGHT_CUSTOM} which is unknown.
+ * <p>
+ * This is the default value when the night mode is set to value other than
+ * {@link #MODE_NIGHT_CUSTOM}.
+ * @hide
+ */
+ @SystemApi
+ public static final int MODE_NIGHT_CUSTOM_TYPE_UNKNOWN = -1;
+
+ /**
+ * A granular type for {@link #MODE_NIGHT_CUSTOM} which is based on a custom schedule.
+ * <p>
+ * This is the default value when night mode is set to {@link #MODE_NIGHT_CUSTOM} unless the
+ * the night mode custom type is specified by calling {@link #setNightModeCustomType(int)}.
+ * @hide
+ */
+ @SystemApi
+ public static final int MODE_NIGHT_CUSTOM_TYPE_SCHEDULE = 0;
+
+ /**
+ * A granular type for {@link #MODE_NIGHT_CUSTOM} which is based on the bedtime schedule.
+ * @hide
+ */
+ @SystemApi
+ public static final int MODE_NIGHT_CUSTOM_TYPE_BEDTIME = 1;
+
private IUiModeManager mService;
/**
@@ -496,6 +535,45 @@ public class UiModeManager {
}
/**
+ * Sets the current night mode to {@link #MODE_NIGHT_CUSTOM} with the custom night mode type
+ * {@code nightModeCustomType}.
+ *
+ * @param nightModeCustomType
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_DAY_NIGHT_MODE)
+ public void setNightModeCustomType(@NightModeCustomType int nightModeCustomType) {
+ if (mService != null) {
+ try {
+ mService.setNightModeCustomType(nightModeCustomType);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * Returns the custom night mode type.
+ * <p>
+ * If the current night mode is not {@link #MODE_NIGHT_CUSTOM}, returns
+ * {@link #MODE_NIGHT_CUSTOM_TYPE_UNKNOWN}.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_DAY_NIGHT_MODE)
+ public int getNightModeCustomType() {
+ if (mService != null) {
+ try {
+ return mService.getNightModeCustomType();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return MODE_NIGHT_CUSTOM_TYPE_UNKNOWN;
+ }
+
+ /**
* Sets and persist the night mode for this application.
* <p>
* The mode can be one of:
@@ -599,11 +677,36 @@ public class UiModeManager {
}
/**
+ * [De]activating night mode for the current user if the current night mode is custom and the
+ * custom type matches {@code nightModeCustomType}.
+ *
+ * @param nightModeCustomType the specify type of custom mode
+ * @param active {@code true} to activate night mode. Otherwise, deactivate night mode
+ * @return {@code true} if night mode has successfully activated for the requested
+ * {@code nightModeCustomType}.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_DAY_NIGHT_MODE)
+ public boolean setNightModeActivatedForCustomMode(@NightModeCustomType int nightModeCustomType,
+ boolean active) {
+ if (mService != null) {
+ try {
+ return mService.setNightModeActivatedForCustomMode(nightModeCustomType, active);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return false;
+ }
+
+ /**
* Activating night mode for the current user
*
* @return {@code true} if the change is successful
* @hide
*/
+ @RequiresPermission(android.Manifest.permission.MODIFY_DAY_NIGHT_MODE)
public boolean setNightModeActivated(boolean active) {
if (mService != null) {
try {
diff --git a/core/java/android/app/WallpaperColors.java b/core/java/android/app/WallpaperColors.java
index 7ef0a19ec44c..067a4c3c047e 100644
--- a/core/java/android/app/WallpaperColors.java
+++ b/core/java/android/app/WallpaperColors.java
@@ -16,6 +16,7 @@
package android.app;
+import android.annotation.FloatRange;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -27,6 +28,7 @@ import android.graphics.drawable.Drawable;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
+import android.util.MathUtils;
import android.util.Size;
import com.android.internal.graphics.ColorUtils;
@@ -44,6 +46,7 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
/**
@@ -173,6 +176,22 @@ public final class WallpaperColors implements Parcelable {
if (bitmap == null) {
throw new IllegalArgumentException("Bitmap can't be null");
}
+ return fromBitmap(bitmap, 0f /* dimAmount */);
+ }
+
+ /**
+ * Constructs {@link WallpaperColors} from a bitmap with dimming applied.
+ * <p>
+ * Main colors will be extracted from the bitmap with dimming taken into account when
+ * calculating dark hints.
+ *
+ * @param bitmap Source where to extract from.
+ * @param dimAmount Wallpaper dim amount
+ * @hide
+ */
+ public static WallpaperColors fromBitmap(@NonNull Bitmap bitmap,
+ @FloatRange (from = 0f, to = 1f) float dimAmount) {
+ Objects.requireNonNull(bitmap, "Bitmap can't be null");
final int bitmapArea = bitmap.getWidth() * bitmap.getHeight();
boolean shouldRecycle = false;
@@ -211,7 +230,7 @@ public final class WallpaperColors implements Parcelable {
}
- int hints = calculateDarkHints(bitmap);
+ int hints = calculateDarkHints(bitmap, dimAmount);
if (shouldRecycle) {
bitmap.recycle();
@@ -507,13 +526,15 @@ public final class WallpaperColors implements Parcelable {
* Checks if image is bright and clean enough to support light text.
*
* @param source What to read.
+ * @param dimAmount How much wallpaper dim amount was applied.
* @return Whether image supports dark text or not.
*/
- private static int calculateDarkHints(Bitmap source) {
+ private static int calculateDarkHints(Bitmap source, float dimAmount) {
if (source == null) {
return 0;
}
+ dimAmount = MathUtils.saturate(dimAmount);
int[] pixels = new int[source.getWidth() * source.getHeight()];
double totalLuminance = 0;
final int maxDarkPixels = (int) (pixels.length * MAX_DARK_AREA);
@@ -521,24 +542,37 @@ public final class WallpaperColors implements Parcelable {
source.getPixels(pixels, 0 /* offset */, source.getWidth(), 0 /* x */, 0 /* y */,
source.getWidth(), source.getHeight());
+ // Create a new black layer with dimAmount as the alpha to be accounted for when computing
+ // the luminance.
+ int dimmingLayerAlpha = (int) (255 * dimAmount);
+ int blackTransparent = ColorUtils.setAlphaComponent(Color.BLACK, dimmingLayerAlpha);
+
// This bitmap was already resized to fit the maximum allowed area.
// Let's just loop through the pixels, no sweat!
float[] tmpHsl = new float[3];
for (int i = 0; i < pixels.length; i++) {
- ColorUtils.colorToHSL(pixels[i], tmpHsl);
- final float luminance = tmpHsl[2];
- final int alpha = Color.alpha(pixels[i]);
+ int pixelColor = pixels[i];
+ ColorUtils.colorToHSL(pixelColor, tmpHsl);
+ final int alpha = Color.alpha(pixelColor);
+
+ // Apply composite colors where the foreground is a black layer with an alpha value of
+ // the dim amount and the background is the wallpaper pixel color.
+ int compositeColors = ColorUtils.compositeColors(blackTransparent, pixelColor);
+
+ // Calculate the adjusted luminance of the dimmed wallpaper pixel color.
+ double adjustedLuminance = ColorUtils.calculateLuminance(compositeColors);
+
// Make sure we don't have a dark pixel mass that will
// make text illegible.
final boolean satisfiesTextContrast = ContrastColorUtil
- .calculateContrast(pixels[i], Color.BLACK) > DARK_PIXEL_CONTRAST;
+ .calculateContrast(pixelColor, Color.BLACK) > DARK_PIXEL_CONTRAST;
if (!satisfiesTextContrast && alpha != 0) {
darkPixels++;
if (DEBUG_DARK_PIXELS) {
pixels[i] = Color.RED;
}
}
- totalLuminance += luminance;
+ totalLuminance += adjustedLuminance;
}
int hints = 0;
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 772492dbfd5e..0a18588e0131 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -16,6 +16,7 @@
package android.app;
+import android.annotation.FloatRange;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -71,6 +72,7 @@ import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
+import android.util.MathUtils;
import android.util.Pair;
import android.view.Display;
import android.view.WindowManagerGlobal;
@@ -1489,27 +1491,18 @@ public class WallpaperManager {
mContext.getUserId());
if (fd != null) {
FileOutputStream fos = null;
- final Bitmap tmp = BitmapFactory.decodeStream(resources.openRawResource(resid));
+ boolean ok = false;
try {
- // If the stream can't be decoded, treat it as an invalid input.
- if (tmp != null) {
- fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
- tmp.compress(Bitmap.CompressFormat.PNG, 100, fos);
- // The 'close()' is the trigger for any server-side image manipulation,
- // so we must do that before waiting for completion.
- fos.close();
- completion.waitForCompletion();
- } else {
- throw new IllegalArgumentException(
- "Resource 0x" + Integer.toHexString(resid) + " is invalid");
- }
+ fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
+ copyStreamToWallpaperFile(resources.openRawResource(resid), fos);
+ // The 'close()' is the trigger for any server-side image manipulation,
+ // so we must do that before waiting for completion.
+ fos.close();
+ completion.waitForCompletion();
} finally {
// Might be redundant but completion shouldn't wait unless the write
// succeeded; this is a fallback if it threw past the close+wait.
IoUtils.closeQuietly(fos);
- if (tmp != null) {
- tmp.recycle();
- }
}
}
} catch (RemoteException e) {
@@ -1751,22 +1744,13 @@ public class WallpaperManager {
result, which, completion, mContext.getUserId());
if (fd != null) {
FileOutputStream fos = null;
- final Bitmap tmp = BitmapFactory.decodeStream(bitmapData);
try {
- // If the stream can't be decoded, treat it as an invalid input.
- if (tmp != null) {
- fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
- tmp.compress(Bitmap.CompressFormat.PNG, 100, fos);
- fos.close();
- completion.waitForCompletion();
- } else {
- throw new IllegalArgumentException("InputStream is invalid");
- }
+ fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
+ copyStreamToWallpaperFile(bitmapData, fos);
+ fos.close();
+ completion.waitForCompletion();
} finally {
IoUtils.closeQuietly(fos);
- if (tmp != null) {
- tmp.recycle();
- }
}
}
} catch (RemoteException e) {
@@ -2012,6 +1996,63 @@ public class WallpaperManager {
}
/**
+ * Sets the wallpaper dim amount between [0f, 1f] which would be blended with the system default
+ * dimming. 0f doesn't add any additional dimming and 1f makes the wallpaper fully black.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.SET_WALLPAPER_DIM_AMOUNT)
+ public void setWallpaperDimAmount(@FloatRange (from = 0f, to = 1f) float dimAmount) {
+ if (sGlobals.mService == null) {
+ Log.w(TAG, "WallpaperService not running");
+ throw new RuntimeException(new DeadSystemException());
+ }
+ try {
+ sGlobals.mService.setWallpaperDimAmount(MathUtils.saturate(dimAmount));
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Gets the current additional dim amount set on the wallpaper. 0f means no application has
+ * added any dimming on top of the system default dim amount.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.SET_WALLPAPER_DIM_AMOUNT)
+ public float getWallpaperDimAmount() {
+ if (sGlobals.mService == null) {
+ Log.w(TAG, "WallpaperService not running");
+ throw new RuntimeException(new DeadSystemException());
+ }
+ try {
+ return sGlobals.mService.getWallpaperDimAmount();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Whether the lock screen wallpaper is different from the system wallpaper.
+ *
+ * @hide
+ */
+ public boolean lockScreenWallpaperExists() {
+ if (sGlobals.mService == null) {
+ Log.w(TAG, "WallpaperService not running");
+ throw new RuntimeException(new DeadSystemException());
+ }
+ try {
+ return sGlobals.mService.lockScreenWallpaperExists();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Set the live wallpaper.
*
* This can only be called by packages with android.permission.SET_WALLPAPER_COMPONENT
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 1de5114dfe1b..96d037c905aa 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -622,6 +622,32 @@ public class DevicePolicyManager {
"android.app.extra.FORCE_UPDATE_ROLE_HOLDER";
/**
+ * A boolean extra indicating whether offline provisioning is allowed.
+ *
+ * <p>For the online provisioning flow, there will be an attempt to download and install
+ * the latest version of the device management role holder. The platform will then delegate
+ * provisioning to the device management role holder via role holder-specific provisioning
+ * actions.
+ *
+ * <p>For the offline provisioning flow, the provisioning flow will always be handled by
+ * the platform.
+ *
+ * <p>If this extra is set to {@code false}, the provisioning flow will enforce that an
+ * internet connection is established, which will start the online provisioning flow. If an
+ * internet connection cannot be established, provisioning will fail.
+ *
+ * <p>If this extra is set to {@code true}, the provisioning flow will still try to connect to
+ * the internet, but if it fails it will start the offline provisioning flow.
+ *
+ * <p>The default value is {@code false}.
+ *
+ * <p>This extra is respected when provided via the provisioning intent actions such as {@link
+ * #ACTION_PROVISION_MANAGED_PROFILE}.
+ */
+ public static final String EXTRA_PROVISIONING_ALLOW_OFFLINE =
+ "android.app.extra.PROVISIONING_ALLOW_OFFLINE";
+
+ /**
* Action: Bugreport sharing with device owner has been accepted by the user.
*
* @hide
@@ -1552,6 +1578,78 @@ public class DevicePolicyManager {
public static final int FLAG_SUPPORTED_MODES_DEVICE_OWNER = 1 << 2;
/**
+ * Constant for {@link #getMinimumRequiredWifiSecurityLevel()} and
+ * {@link #setMinimumRequiredWifiSecurityLevel(int)}: no minimum security level.
+ *
+ * <p> When returned from {@link #getMinimumRequiredWifiSecurityLevel()}, the constant
+ * represents the current minimum security level required.
+ * When passed to {@link #setMinimumRequiredWifiSecurityLevel(int)}, it sets the
+ * minimum security level a Wi-Fi network must meet.
+ *
+ * @see #WIFI_SECURITY_PERSONAL
+ * @see #WIFI_SECURITY_ENTERPRISE_EAP
+ * @see #WIFI_SECURITY_ENTERPRISE_192
+ */
+ public static final int WIFI_SECURITY_OPEN = 0;
+
+ /**
+ * Constant for {@link #getMinimumRequiredWifiSecurityLevel()} and
+ * {@link #setMinimumRequiredWifiSecurityLevel(int)}: personal network such as WEP, WPA2-PSK.
+ *
+ * <p> When returned from {@link #getMinimumRequiredWifiSecurityLevel()}, the constant
+ * represents the current minimum security level required.
+ * When passed to {@link #setMinimumRequiredWifiSecurityLevel(int)}, it sets the
+ * minimum security level a Wi-Fi network must meet.
+ *
+ * @see #WIFI_SECURITY_OPEN
+ * @see #WIFI_SECURITY_ENTERPRISE_EAP
+ * @see #WIFI_SECURITY_ENTERPRISE_192
+ */
+ public static final int WIFI_SECURITY_PERSONAL = 1;
+
+ /**
+ * Constant for {@link #getMinimumRequiredWifiSecurityLevel()} and
+ * {@link #setMinimumRequiredWifiSecurityLevel(int)}: enterprise EAP network.
+ *
+ * <p> When returned from {@link #getMinimumRequiredWifiSecurityLevel()}, the constant
+ * represents the current minimum security level required.
+ * When passed to {@link #setMinimumRequiredWifiSecurityLevel(int)}, it sets the
+ * minimum security level a Wi-Fi network must meet.
+ *
+ * @see #WIFI_SECURITY_OPEN
+ * @see #WIFI_SECURITY_PERSONAL
+ * @see #WIFI_SECURITY_ENTERPRISE_192
+ */
+ public static final int WIFI_SECURITY_ENTERPRISE_EAP = 2;
+
+ /**
+ * Constant for {@link #getMinimumRequiredWifiSecurityLevel()} and
+ * {@link #setMinimumRequiredWifiSecurityLevel(int)}: enterprise 192 bit network.
+ *
+ * <p> When returned from {@link #getMinimumRequiredWifiSecurityLevel()}, the constant
+ * represents the current minimum security level required.
+ * When passed to {@link #setMinimumRequiredWifiSecurityLevel(int)}, it sets the
+ * minimum security level a Wi-Fi network must meet.
+ *
+ * @see #WIFI_SECURITY_OPEN
+ * @see #WIFI_SECURITY_PERSONAL
+ * @see #WIFI_SECURITY_ENTERPRISE_EAP
+ */
+ public static final int WIFI_SECURITY_ENTERPRISE_192 = 3;
+
+ /**
+ * Possible Wi-Fi minimum security levels
+ *
+ * @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"WIFI_SECURITY_"}, value = {
+ WIFI_SECURITY_OPEN,
+ WIFI_SECURITY_PERSONAL,
+ WIFI_SECURITY_ENTERPRISE_EAP,
+ WIFI_SECURITY_ENTERPRISE_192})
+ public @interface WifiSecurity {}
+
+ /**
* This MIME type is used for starting the device owner provisioning.
*
* <p>During device owner provisioning a device admin app is set as the owner of the device.
@@ -2986,6 +3084,54 @@ public class DevicePolicyManager {
"android.app.extra.PROVISIONING_ROLE_HOLDER_CUSTOM_USER_CONSENT_INTENT";
/**
+ * Activity action: attempts to establish network connection
+ *
+ * <p>This intent can be accompanied by any of the relevant provisioning extras related to
+ * network connectivity, such as:
+ * <ul>
+ * <li>{@link #EXTRA_PROVISIONING_WIFI_SSID}</li>
+ * <li>{@link #EXTRA_PROVISIONING_WIFI_HIDDEN}</li>
+ * <li>{@link #EXTRA_PROVISIONING_WIFI_SECURITY_TYPE}</li>
+ * <li>{@link #EXTRA_PROVISIONING_WIFI_PASSWORD}</li>
+ * <li>{@link #EXTRA_PROVISIONING_WIFI_PROXY_HOST}</li>
+ * <li>{@link #EXTRA_PROVISIONING_WIFI_PROXY_PORT}</li>
+ * <li>{@link #EXTRA_PROVISIONING_WIFI_PROXY_BYPASS}</li>
+ * <li>{@link #EXTRA_PROVISIONING_WIFI_PAC_URL}</li>
+ * <li>{@code #EXTRA_PROVISIONING_WIFI_EAP_METHOD}</li>
+ * <li>{@code #EXTRA_PROVISIONING_WIFI_PHASE2_AUTH}</li>
+ * <li>{@code #EXTRA_PROVISIONING_WIFI_CA_CERTIFICATE}</li>
+ * <li>{@code #EXTRA_PROVISIONING_WIFI_USER_CERTIFICATE}</li>
+ * <li>{@code #EXTRA_PROVISIONING_WIFI_IDENTITY}</li>
+ * <li>{@code #EXTRA_PROVISIONING_WIFI_ANONYMOUS_IDENTITY}</li>
+ * <li>{@code #EXTRA_PROVISIONING_WIFI_DOMAIN}</li>
+ * </ul>
+ *
+ * <p>If there are provisioning extras related to network connectivity, this activity
+ * attempts to connect to the specified network. Otherwise it prompts the end-user to connect.
+ *
+ * <p>This activity is meant to be started by the provisioning initiator prior to starting
+ * {@link #ACTION_PROVISION_MANAGED_PROFILE} or {@link
+ * #ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE}.
+ *
+ * <p>Note that network connectivity is still also handled when provisioning via {@link
+ * #ACTION_PROVISION_MANAGED_PROFILE} or {@link
+ * #ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE}. {@link
+ * #ACTION_ESTABLISH_NETWORK_CONNECTION} should only be used in cases when the provisioning
+ * initiator would like to do some additional logic after the network connectivity step and
+ * before the start of provisioning.
+ *
+ * If network connection is established, {@link Activity#RESULT_OK} will be returned. Otherwise
+ * the result will be {@link Activity#RESULT_CANCELED}.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.DISPATCH_PROVISIONING_MESSAGE)
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ @SystemApi
+ public static final String ACTION_ESTABLISH_NETWORK_CONNECTION =
+ "android.app.action.ESTABLISH_NETWORK_CONNECTION";
+
+ /**
* Maximum supported password length. Kind-of arbitrary.
* @hide
*/
@@ -3256,14 +3402,15 @@ public class DevicePolicyManager {
/**
* Broadcast action: notify system apps (e.g. settings, SysUI, etc) that the device management
- * resources with IDs {@link #EXTRA_RESOURCE_ID} has been updated using, the updated resources
- * can be retrieved using {@link #getDrawable}.
+ * resources with IDs {@link #EXTRA_RESOURCE_ID} has been updated, the updated resources can be
+ * retrieved using {@link #getDrawable} and {@code #getString}.
*
* <p>This broadcast is sent to registered receivers only.
*
* <p> The following extras will be included to identify the type of resource being updated:
* <ul>
* <li>{@link #EXTRA_RESOURCE_TYPE_DRAWABLE} for drawable resources</li>
+ * <li>{@link #EXTRA_RESOURCE_TYPE_STRING} for string resources</li>
* </ul>
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
@@ -3278,8 +3425,16 @@ public class DevicePolicyManager {
"android.app.extra.RESOURCE_TYPE_DRAWABLE";
/**
+ * A boolean extra for {@link #ACTION_DEVICE_POLICY_RESOURCE_UPDATED} to indicate that a
+ * resource of type {@link String} is being updated.
+ */
+ public static final String EXTRA_RESOURCE_TYPE_STRING =
+ "android.app.extra.RESOURCE_TYPE_STRING";
+
+ /**
* An integer array extra for {@link #ACTION_DEVICE_POLICY_RESOURCE_UPDATED} to indicate which
- * drawable IDs (see {@link DevicePolicyResources.UpdatableDrawableId}) have been updated.
+ * resource IDs (see {@link DevicePolicyResources.UpdatableDrawableId} and
+ * {@link DevicePolicyResources.UpdatableStringId}) have been updated.
*/
public static final String EXTRA_RESOURCE_ID =
"android.app.extra.RESOURCE_ID";
@@ -14482,6 +14637,105 @@ public class DevicePolicyManager {
}
/**
+ * Called by device owner or profile owner of an organization-owned managed profile to
+ * specify the minimum security level required for Wi-Fi networks.
+ * The device may not connect to networks that do not meet the minimum security level.
+ * If the current network does not meet the minimum security level set, it will be disconnected.
+ *
+ *
+ * @param level minimum security level
+ * @throws SecurityException if the caller is not a device owner or a profile owner on
+ * an organization-owned managed profile.
+ */
+ public void setMinimumRequiredWifiSecurityLevel(@WifiSecurity int level) {
+ throwIfParentInstance("setMinimumRequiredWifiSecurityLevel");
+ if (mService != null) {
+ try {
+ mService.setMinimumRequiredWifiSecurityLevel(level);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * Returns the current Wi-Fi minimum security level.
+ *
+ * @see #setMinimumRequiredWifiSecurityLevel(int)
+ */
+ public @WifiSecurity int getMinimumRequiredWifiSecurityLevel() {
+ throwIfParentInstance("getMinimumRequiredWifiSecurityLevel");
+ if (mService == null) {
+ return WIFI_SECURITY_OPEN;
+ }
+ try {
+ return mService.getMinimumRequiredWifiSecurityLevel();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Called by device owner or profile owner of an organization-owned managed profile to
+ * specify the Wi-Fi SSID policy ({@link WifiSsidPolicy}).
+ * Wi-Fi SSID policy specifies the SSID restriction the network must satisfy
+ * in order to be eligible for a connection. Providing a null policy results in the
+ * deactivation of the SSID restriction
+ *
+ * @param policy Wi-Fi SSID policy
+ * @throws SecurityException if the caller is not a device owner or a profile owner on
+ * an organization-owned managed profile.
+ */
+ public void setWifiSsidPolicy(@Nullable WifiSsidPolicy policy) {
+ throwIfParentInstance("setWifiSsidPolicy");
+ if (mService != null) {
+ try {
+ if (policy == null) {
+ mService.setSsidAllowlist(new ArrayList<>());
+ } else {
+ int policyType = policy.getPolicyType();
+ if (policyType == WifiSsidPolicy.WIFI_SSID_POLICY_TYPE_ALLOWLIST) {
+ mService.setSsidAllowlist(new ArrayList<>(policy.getSsids()));
+ } else {
+ mService.setSsidDenylist(new ArrayList<>(policy.getSsids()));
+ }
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * Returns the current Wi-Fi SSID policy.
+ * If the policy has not been set, it will return NULL.
+ *
+ * @see #setWifiSsidPolicy(WifiSsidPolicy)
+ * @throws SecurityException if the caller is not a device owner or a profile owner on
+ * an organization-owned managed profile or a system app.
+ */
+ @Nullable
+ public WifiSsidPolicy getWifiSsidPolicy() {
+ throwIfParentInstance("getWifiSsidPolicy");
+ if (mService == null) {
+ return null;
+ }
+ try {
+ List<String> allowlist = mService.getSsidAllowlist();
+ if (!allowlist.isEmpty()) {
+ return WifiSsidPolicy.createAllowlistPolicy(new ArraySet<>(allowlist));
+ }
+ List<String> denylist = mService.getSsidDenylist();
+ if (!denylist.isEmpty()) {
+ return WifiSsidPolicy.createDenylistPolicy(new ArraySet<>(denylist));
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ return null;
+ }
+
+ /**
* For each {@link DevicePolicyDrawableResource} item in {@code drawables}, if
* {@link DevicePolicyDrawableResource#getDrawableSource()} is not set or is set to
* {@link DevicePolicyResources.Drawable.Source#UNDEFINED}, it updates the drawable resource for
@@ -14514,11 +14768,6 @@ public class DevicePolicyManager {
*
* @param drawables The list of {@link DevicePolicyDrawableResource} to update.
*
- * @throws IllegalArgumentException if {@link DevicePolicyDrawableResource#getDrawableId()},
- * {@link DevicePolicyDrawableResource#getDrawableStyle()}, or
- * {@link DevicePolicyDrawableResource#getDrawableSource()} aren't defined in
- * {@link DevicePolicyResources.Drawable}.
- *
* @hide
*/
@SystemApi
@@ -14546,9 +14795,6 @@ public class DevicePolicyManager {
*
* @param drawableIds The list of IDs to remove.
*
- * @throws IllegalArgumentException if IDs are not defined in
- * {@link DevicePolicyResources.Drawable}
- *
* @hide
*/
@SystemApi
@@ -14572,6 +14818,9 @@ public class DevicePolicyManager {
* <p>Also returns the drawable from {@code defaultDrawableLoader} if
* {@link DevicePolicyResources.Drawable#INVALID_ID} was passed.
*
+ * <p>{@code defaultDrawableLoader} must return a non {@code null} {@link Drawable}, otherwise a
+ * {@link NullPointerException} is thrown.
+ *
* <p>This API uses the screen density returned from {@link Resources#getConfiguration()}, to
* set a different value use
* {@link #getDrawableForDensity(int, int, int, Callable)}.
@@ -14587,7 +14836,7 @@ public class DevicePolicyManager {
* @param defaultDrawableLoader To get the default drawable if no updated drawable was set for
* the provided params.
*/
- @Nullable
+ @NonNull
public Drawable getDrawable(
@DevicePolicyResources.UpdatableDrawableId int drawableId,
@DevicePolicyResources.UpdatableDrawableStyle int drawableStyle,
@@ -14601,6 +14850,9 @@ public class DevicePolicyManager {
* could result in returning a different drawable than {@link #getDrawable(int, int, Callable)}
* if an override was set for that specific source.
*
+ * <p>{@code defaultDrawableLoader} must return a non {@code null} {@link Drawable}, otherwise a
+ * {@link NullPointerException} is thrown.
+ *
* <p>Callers should register for {@link #ACTION_DEVICE_POLICY_RESOURCE_UPDATED} to get
* notified when a resource has been updated.
*
@@ -14610,7 +14862,7 @@ public class DevicePolicyManager {
* @param defaultDrawableLoader To get the default drawable if no updated drawable was set for
* the provided params.
*/
- @Nullable
+ @NonNull
public Drawable getDrawable(
@DevicePolicyResources.UpdatableDrawableId int drawableId,
@DevicePolicyResources.UpdatableDrawableStyle int drawableStyle,
@@ -14648,6 +14900,9 @@ public class DevicePolicyManager {
* Similar to {@link #getDrawable(int, int, Callable)}, but also accepts
* {@code density}. See {@link Resources#getDrawableForDensity(int, int, Resources.Theme)}.
*
+ * <p>{@code defaultDrawableLoader} must return a non {@code null} {@link Drawable}, otherwise a
+ * {@link NullPointerException} is thrown.
+ *
* <p>Callers should register for {@link #ACTION_DEVICE_POLICY_RESOURCE_UPDATED} to get
* notified when a resource has been updated.
*
@@ -14659,7 +14914,7 @@ public class DevicePolicyManager {
* @param defaultDrawableLoader To get the default drawable if no updated drawable was set for
* the provided params.
*/
- @Nullable
+ @NonNull
public Drawable getDrawableForDensity(
@DevicePolicyResources.UpdatableDrawableId int drawableId,
@DevicePolicyResources.UpdatableDrawableStyle int drawableStyle,
@@ -14677,6 +14932,9 @@ public class DevicePolicyManager {
* Similar to {@link #getDrawable(int, int, int, Callable)}, but also accepts
* {@code density}. See {@link Resources#getDrawableForDensity(int, int, Resources.Theme)}.
*
+ * <p>{@code defaultDrawableLoader} must return a non {@code null} {@link Drawable}, otherwise a
+ * {@link NullPointerException} is thrown.
+ *
* <p>Callers should register for {@link #ACTION_DEVICE_POLICY_RESOURCE_UPDATED} to get
* notified when a resource has been updated.
*
@@ -14689,7 +14947,7 @@ public class DevicePolicyManager {
* @param defaultDrawableLoader To get the default drawable if no updated drawable was set for
* the provided params.
*/
- @Nullable
+ @NonNull
public Drawable getDrawableForDensity(
@DevicePolicyResources.UpdatableDrawableId int drawableId,
@DevicePolicyResources.UpdatableDrawableStyle int drawableStyle,
@@ -14719,4 +14977,167 @@ public class DevicePolicyManager {
}
return ParcelableResource.loadDefaultDrawable(defaultDrawableLoader);
}
+
+ /**
+ * For each {@link DevicePolicyStringResource} item in {@code strings}, it updates the string
+ * resource for {@link DevicePolicyStringResource#getStringId()} to the string with ID
+ * {@code callingPackageResourceId} (see {@link DevicePolicyResources.String}), meaning any
+ * system UI surface calling {@link #getString} with {@code stringId} will get
+ * the new resource after this API is called.
+ *
+ * <p>Sends a broadcast with action {@link #ACTION_DEVICE_POLICY_RESOURCE_UPDATED} to
+ * registered receivers when a resource has been updated successfully.
+ *
+ * <p>Important notes to consider when using this API:
+ * <ul>
+ * <li> {@link #getString} references the resource
+ * {@code callingPackageResourceId} in the calling package each time it gets called. You have to
+ * ensure that the resource is always available in the calling package as long as it is used as
+ * an updated resource.
+ * <li> You still have to re-call {@code setStrings} even if you only make changes to the
+ * content of the resource with ID {@code callingPackageResourceId} as the content might be
+ * cached and would need updating.
+ * </ul>
+ *
+ * @param strings The list of {@link DevicePolicyStringResource} to update.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES)
+ public void setStrings(@NonNull Set<DevicePolicyStringResource> strings) {
+ if (mService != null) {
+ try {
+ mService.setStrings(new ArrayList<>(strings));
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * Removes the updated strings for the list of {@code stringIds} (see
+ * {@link DevicePolicyResources.String}) that was previously set by calling {@link #setStrings},
+ * meaning any subsequent calls to {@link #getString} for the provided IDs will
+ * return the default string from {@code defaultStringLoader}.
+ *
+ * <p>Sends a broadcast with action {@link #ACTION_DEVICE_POLICY_RESOURCE_UPDATED} to
+ * registered receivers when a resource has been reset successfully.
+ *
+ * @param stringIds The list of IDs to remove the updated resources for.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES)
+ public void resetStrings(@NonNull String[] stringIds) {
+ if (mService != null) {
+ try {
+ mService.resetStrings(stringIds);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * Returns the appropriate updated string for the {@code stringId} (see
+ * {@link DevicePolicyResources.String}) if one was set using
+ * {@link #setStrings}, otherwise returns the string from {@code defaultStringLoader}.
+ *
+ * <p>Also returns the string from {@code defaultStringLoader} if
+ * {@link DevicePolicyResources.String#INVALID_ID} was passed.
+ *
+ * <p>{@code defaultStringLoader} must return a non {@code null} {@link String}, otherwise a
+ * {@link NullPointerException} is thrown.
+ *
+ * <p>Callers should register for {@link #ACTION_DEVICE_POLICY_RESOURCE_UPDATED} to get
+ * notified when a resource has been updated.
+ *
+ * <p>Note that each call to this API loads the resource from the package that called
+ * {@link #setStrings} to set the updated resource.
+ *
+ * @param stringId The IDs to get the updated resource for.
+ * @param defaultStringLoader To get the default string if no updated string was set for
+ * {@code stringId}.
+ *
+ * @hide
+ */
+ @SystemApi
+ @NonNull
+ public String getString(
+ @NonNull @DevicePolicyResources.UpdatableStringId String stringId,
+ @NonNull Callable<String> defaultStringLoader) {
+
+ Objects.requireNonNull(stringId, "stringId can't be null");
+ Objects.requireNonNull(defaultStringLoader, "defaultStringLoader can't be null");
+
+ if (stringId.equals(DevicePolicyResources.Strings.UNDEFINED)) {
+ return ParcelableResource.loadDefaultString(defaultStringLoader);
+ }
+ if (mService != null) {
+ try {
+ ParcelableResource resource = mService.getString(stringId);
+ if (resource == null) {
+ return ParcelableResource.loadDefaultString(defaultStringLoader);
+ }
+ return resource.getString(mContext, defaultStringLoader);
+ } catch (RemoteException e) {
+ Log.e(
+ TAG,
+ "Error getting the updated string from DevicePolicyManagerService.",
+ e);
+ return ParcelableResource.loadDefaultString(defaultStringLoader);
+ }
+ }
+ return ParcelableResource.loadDefaultString(defaultStringLoader);
+ }
+
+ /**
+ * Similar to {@link #getString(String, Callable)} but accepts {@code formatArgs} and returns a
+ * localized formatted string, substituting the format arguments as defined in
+ * {@link java.util.Formatter} and {@link java.lang.String#format}, (see
+ * {@link Resources#getString(int, Object...)}).
+ *
+ * <p>{@code defaultStringLoader} must return a non {@code null} {@link String}, otherwise a
+ * {@link NullPointerException} is thrown.
+ *
+ * @param stringId The IDs to get the updated resource for.
+ * @param defaultStringLoader To get the default string if no updated string was set for
+ * {@code stringId}.
+ * @param formatArgs The format arguments that will be used for substitution.
+ *
+ * @hide
+ */
+ @SystemApi
+ @NonNull
+ @SuppressLint("SamShouldBeLast")
+ public String getString(
+ @NonNull @DevicePolicyResources.UpdatableStringId String stringId,
+ @NonNull Callable<String> defaultStringLoader,
+ @NonNull Object... formatArgs) {
+
+ Objects.requireNonNull(stringId, "stringId can't be null");
+ Objects.requireNonNull(defaultStringLoader, "defaultStringLoader can't be null");
+
+ if (stringId.equals(DevicePolicyResources.Strings.UNDEFINED)) {
+ return ParcelableResource.loadDefaultString(defaultStringLoader);
+ }
+ if (mService != null) {
+ try {
+ ParcelableResource resource = mService.getString(stringId);
+ if (resource == null) {
+ return ParcelableResource.loadDefaultString(defaultStringLoader);
+ }
+ return resource.getString(mContext, defaultStringLoader, formatArgs);
+ } catch (RemoteException e) {
+ Log.e(
+ TAG,
+ "Error getting the updated string from DevicePolicyManagerService.",
+ e);
+ return ParcelableResource.loadDefaultString(defaultStringLoader);
+ }
+ }
+ return ParcelableResource.loadDefaultString(defaultStringLoader);
+ }
}
diff --git a/core/java/android/app/admin/DevicePolicyResources.java b/core/java/android/app/admin/DevicePolicyResources.java
index 5133f26f8111..46e2cceefaf7 100644
--- a/core/java/android/app/admin/DevicePolicyResources.java
+++ b/core/java/android/app/admin/DevicePolicyResources.java
@@ -16,8 +16,123 @@
package android.app.admin;
+import static android.app.admin.DevicePolicyResources.Strings.Core.CANT_ADD_ACCOUNT_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.FORWARD_INTENT_TO_PERSONAL;
+import static android.app.admin.DevicePolicyResources.Strings.Core.FORWARD_INTENT_TO_WORK;
+import static android.app.admin.DevicePolicyResources.Strings.Core.LOCATION_CHANGED_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.LOCATION_CHANGED_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.NETWORK_LOGGING_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.NETWORK_LOGGING_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.NOTIFICATION_CHANNEL_DEVICE_ADMIN;
+import static android.app.admin.DevicePolicyResources.Strings.Core.NOTIFICATION_WORK_PROFILE_CONTENT_DESCRIPTION;
+import static android.app.admin.DevicePolicyResources.Strings.Core.PACKAGE_DELETED_BY_DO;
+import static android.app.admin.DevicePolicyResources.Strings.Core.PACKAGE_INSTALLED_BY_DO;
+import static android.app.admin.DevicePolicyResources.Strings.Core.PACKAGE_UPDATED_BY_DO;
+import static android.app.admin.DevicePolicyResources.Strings.Core.PERSONAL_APP_SUSPENSION_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.PERSONAL_APP_SUSPENSION_SOON_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.PERSONAL_APP_SUSPENSION_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.PERSONAL_APP_SUSPENSION_TURN_ON_PROFILE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.PRINTING_DISABLED_NAMED_ADMIN;
+import static android.app.admin.DevicePolicyResources.Strings.Core.PROFILE_ENCRYPTED_DETAIL;
+import static android.app.admin.DevicePolicyResources.Strings.Core.PROFILE_ENCRYPTED_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.PROFILE_ENCRYPTED_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_ACCESS_PERSONAL;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_ACCESS_WORK;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_SHARE_WITH_PERSONAL;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_SHARE_WITH_WORK;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CROSS_PROFILE_BLOCKED_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_NO_PERSONAL_APPS;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_NO_WORK_APPS;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_PERSONAL_TAB;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_PERSONAL_TAB_ACCESSIBILITY;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_WORK_PAUSED_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_WORK_PROFILE_NOT_SUPPORTED;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_WORK_TAB;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_WORK_TAB_ACCESSIBILITY;
+import static android.app.admin.DevicePolicyResources.Strings.Core.SWITCH_TO_PERSONAL_LABEL;
+import static android.app.admin.DevicePolicyResources.Strings.Core.SWITCH_TO_WORK_LABEL;
+import static android.app.admin.DevicePolicyResources.Strings.Core.UNLAUNCHABLE_APP_WORK_PAUSED_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.UNLAUNCHABLE_APP_WORK_PAUSED_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.WORK_PROFILE_BADGED_LABEL;
+import static android.app.admin.DevicePolicyResources.Strings.Core.WORK_PROFILE_DELETED_FAILED_PASSWORD_ATTEMPTS_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.WORK_PROFILE_DELETED_GENERIC_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.WORK_PROFILE_DELETED_ORG_OWNED_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.WORK_PROFILE_DELETED_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.DocumentsUi.CANT_SAVE_TO_PERSONAL_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.DocumentsUi.CANT_SAVE_TO_PERSONAL_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.DocumentsUi.CANT_SAVE_TO_WORK_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.DocumentsUi.CANT_SAVE_TO_WORK_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.DocumentsUi.CANT_SELECT_PERSONAL_FILES_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.DocumentsUi.CANT_SELECT_PERSONAL_FILES_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.DocumentsUi.CANT_SELECT_WORK_FILES_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.DocumentsUi.CANT_SELECT_WORK_FILES_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.DocumentsUi.CROSS_PROFILE_NOT_ALLOWED_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.DocumentsUi.CROSS_PROFILE_NOT_ALLOWED_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.DocumentsUi.PERSONAL_TAB;
+import static android.app.admin.DevicePolicyResources.Strings.DocumentsUi.PREVIEW_WORK_FILE_ACCESSIBILITY;
+import static android.app.admin.DevicePolicyResources.Strings.DocumentsUi.WORK_ACCESSIBILITY;
+import static android.app.admin.DevicePolicyResources.Strings.DocumentsUi.WORK_PROFILE_OFF_ENABLE_BUTTON;
+import static android.app.admin.DevicePolicyResources.Strings.DocumentsUi.WORK_PROFILE_OFF_ERROR_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.DocumentsUi.WORK_TAB;
+import static android.app.admin.DevicePolicyResources.Strings.Launcher.ALL_APPS_PERSONAL_TAB;
+import static android.app.admin.DevicePolicyResources.Strings.Launcher.ALL_APPS_PERSONAL_TAB_ACCESSIBILITY;
+import static android.app.admin.DevicePolicyResources.Strings.Launcher.ALL_APPS_WORK_TAB;
+import static android.app.admin.DevicePolicyResources.Strings.Launcher.ALL_APPS_WORK_TAB_ACCESSIBILITY;
+import static android.app.admin.DevicePolicyResources.Strings.Launcher.DISABLED_BY_ADMIN_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.Launcher.WIDGETS_PERSONAL_TAB;
+import static android.app.admin.DevicePolicyResources.Strings.Launcher.WIDGETS_WORK_TAB;
+import static android.app.admin.DevicePolicyResources.Strings.Launcher.WORK_FOLDER_NAME;
+import static android.app.admin.DevicePolicyResources.Strings.Launcher.WORK_PROFILE_EDU;
+import static android.app.admin.DevicePolicyResources.Strings.Launcher.WORK_PROFILE_EDU_ACCEPT;
+import static android.app.admin.DevicePolicyResources.Strings.Launcher.WORK_PROFILE_ENABLE_BUTTON;
+import static android.app.admin.DevicePolicyResources.Strings.Launcher.WORK_PROFILE_PAUSED_DESCRIPTION;
+import static android.app.admin.DevicePolicyResources.Strings.Launcher.WORK_PROFILE_PAUSE_BUTTON;
+import static android.app.admin.DevicePolicyResources.Strings.MediaProvider.BLOCKED_BY_ADMIN_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.MediaProvider.BLOCKED_FROM_PERSONAL_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.MediaProvider.BLOCKED_FROM_WORK_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.MediaProvider.SWITCH_TO_PERSONAL_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.MediaProvider.SWITCH_TO_WORK_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.MediaProvider.WORK_PROFILE_PAUSED_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.SystemUi.BIOMETRIC_DIALOG_WORK_LOCK_FAILED_ATTEMPTS;
+import static android.app.admin.DevicePolicyResources.Strings.SystemUi.BIOMETRIC_DIALOG_WORK_PASSWORD_LAST_ATTEMPT;
+import static android.app.admin.DevicePolicyResources.Strings.SystemUi.BIOMETRIC_DIALOG_WORK_PATTERN_LAST_ATTEMPT;
+import static android.app.admin.DevicePolicyResources.Strings.SystemUi.BIOMETRIC_DIALOG_WORK_PIN_LAST_ATTEMPT;
+import static android.app.admin.DevicePolicyResources.Strings.SystemUi.KEYGUARD_MANAGEMENT_DISCLOSURE;
+import static android.app.admin.DevicePolicyResources.Strings.SystemUi.KEYGUARD_NAMED_MANAGEMENT_DISCLOSURE;
+import static android.app.admin.DevicePolicyResources.Strings.SystemUi.ONGOING_PRIVACY_DIALOG_WORK;
+import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_DIALOG_MANAGEMENT;
+import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_DIALOG_MANAGEMENT_CA_CERT;
+import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_DIALOG_MANAGEMENT_NAMED_VPN;
+import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_DIALOG_MANAGEMENT_NETWORK;
+import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_DIALOG_MANAGEMENT_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_DIALOG_MANAGEMENT_TWO_NAMED_VPN;
+import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_DIALOG_NAMED_MANAGEMENT;
+import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_DIALOG_PERSONAL_PROFILE_NAMED_VPN;
+import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_DIALOG_VIEW_POLICIES;
+import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_DIALOG_WORK_PROFILE_CA_CERT;
+import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_DIALOG_WORK_PROFILE_NAMED_VPN;
+import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_DIALOG_WORK_PROFILE_NETWORK;
+import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_MSG_MANAGEMENT;
+import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_MSG_MANAGEMENT_MONITORING;
+import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_MSG_MANAGEMENT_MULTIPLE_VPNS;
+import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_MSG_MANAGEMENT_NAMED_VPN;
+import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_MSG_NAMED_MANAGEMENT;
+import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_MSG_NAMED_MANAGEMENT_MONITORING;
+import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_MSG_NAMED_MANAGEMENT_MULTIPLE_VPNS;
+import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_MSG_NAMED_MANAGEMENT_NAMED_VPN;
+import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_MSG_NAMED_WORK_PROFILE_MONITORING;
+import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_MSG_PERSONAL_PROFILE_NAMED_VPN;
+import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_MSG_WORK_PROFILE_MONITORING;
+import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_MSG_WORK_PROFILE_NAMED_VPN;
+import static android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_MSG_WORK_PROFILE_NETWORK;
+import static android.app.admin.DevicePolicyResources.Strings.SystemUi.STATUS_BAR_WORK_ICON_ACCESSIBILITY;
+import static android.app.admin.DevicePolicyResources.Strings.SystemUi.WORK_LOCK_ACCESSIBILITY;
+
import android.annotation.IntDef;
+import android.annotation.StringDef;
import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.os.UserHandle;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -27,8 +142,8 @@ import java.util.Set;
/**
* Class containing the required identifiers to update device management resources.
*
- * <p>See {@link DevicePolicyManager#getDrawable}.
- *
+ * <p>See {@link DevicePolicyManager#getDrawable} and
+ * {@code DevicePolicyManager#getString}.
*/
public final class DevicePolicyResources {
@@ -78,6 +193,74 @@ public final class DevicePolicyResources {
})
public @interface UpdatableDrawableSource {}
+ /**
+ * Resource identifiers used to update device management-related string resources.
+ *
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @StringDef({
+ // Launcher Strings
+ WORK_PROFILE_EDU, WORK_PROFILE_EDU_ACCEPT, Strings.Launcher.WORK_PROFILE_PAUSED_TITLE,
+ WORK_PROFILE_PAUSED_DESCRIPTION, WORK_PROFILE_PAUSE_BUTTON, WORK_PROFILE_ENABLE_BUTTON,
+ ALL_APPS_WORK_TAB, ALL_APPS_PERSONAL_TAB, ALL_APPS_WORK_TAB_ACCESSIBILITY,
+ ALL_APPS_PERSONAL_TAB_ACCESSIBILITY, WORK_FOLDER_NAME, WIDGETS_WORK_TAB,
+ WIDGETS_PERSONAL_TAB, DISABLED_BY_ADMIN_MESSAGE,
+
+ // SysUI Strings
+ QS_MSG_MANAGEMENT, QS_MSG_NAMED_MANAGEMENT, QS_MSG_MANAGEMENT_MONITORING,
+ QS_MSG_NAMED_MANAGEMENT_MONITORING, QS_MSG_MANAGEMENT_NAMED_VPN,
+ QS_MSG_NAMED_MANAGEMENT_NAMED_VPN, QS_MSG_MANAGEMENT_MULTIPLE_VPNS,
+ QS_MSG_NAMED_MANAGEMENT_MULTIPLE_VPNS, QS_MSG_WORK_PROFILE_MONITORING,
+ QS_MSG_NAMED_WORK_PROFILE_MONITORING, QS_MSG_WORK_PROFILE_NETWORK,
+ QS_MSG_WORK_PROFILE_NAMED_VPN, QS_MSG_PERSONAL_PROFILE_NAMED_VPN,
+ QS_DIALOG_MANAGEMENT_TITLE, QS_DIALOG_VIEW_POLICIES, QS_DIALOG_MANAGEMENT,
+ QS_DIALOG_NAMED_MANAGEMENT, QS_DIALOG_MANAGEMENT_CA_CERT,
+ QS_DIALOG_WORK_PROFILE_CA_CERT, QS_DIALOG_MANAGEMENT_NETWORK,
+ QS_DIALOG_WORK_PROFILE_NETWORK, QS_DIALOG_MANAGEMENT_NAMED_VPN,
+ QS_DIALOG_MANAGEMENT_TWO_NAMED_VPN, QS_DIALOG_WORK_PROFILE_NAMED_VPN,
+ QS_DIALOG_PERSONAL_PROFILE_NAMED_VPN, BIOMETRIC_DIALOG_WORK_PIN_LAST_ATTEMPT,
+ BIOMETRIC_DIALOG_WORK_PATTERN_LAST_ATTEMPT, BIOMETRIC_DIALOG_WORK_PASSWORD_LAST_ATTEMPT,
+ BIOMETRIC_DIALOG_WORK_LOCK_FAILED_ATTEMPTS, STATUS_BAR_WORK_ICON_ACCESSIBILITY,
+ ONGOING_PRIVACY_DIALOG_WORK, KEYGUARD_MANAGEMENT_DISCLOSURE,
+ KEYGUARD_NAMED_MANAGEMENT_DISCLOSURE, WORK_LOCK_ACCESSIBILITY,
+
+ // Core Strings
+ WORK_PROFILE_DELETED_TITLE, WORK_PROFILE_DELETED_FAILED_PASSWORD_ATTEMPTS_MESSAGE,
+ WORK_PROFILE_DELETED_GENERIC_MESSAGE, WORK_PROFILE_DELETED_ORG_OWNED_MESSAGE,
+ PERSONAL_APP_SUSPENSION_TITLE, PERSONAL_APP_SUSPENSION_MESSAGE,
+ PERSONAL_APP_SUSPENSION_SOON_MESSAGE, PERSONAL_APP_SUSPENSION_TURN_ON_PROFILE,
+ PRINTING_DISABLED_NAMED_ADMIN, LOCATION_CHANGED_TITLE, LOCATION_CHANGED_MESSAGE,
+ NETWORK_LOGGING_TITLE, NETWORK_LOGGING_MESSAGE,
+ NOTIFICATION_WORK_PROFILE_CONTENT_DESCRIPTION, NOTIFICATION_CHANNEL_DEVICE_ADMIN,
+ SWITCH_TO_WORK_LABEL, SWITCH_TO_PERSONAL_LABEL, FORWARD_INTENT_TO_WORK,
+ FORWARD_INTENT_TO_PERSONAL, RESOLVER_WORK_PROFILE_NOT_SUPPORTED, RESOLVER_PERSONAL_TAB,
+ RESOLVER_WORK_TAB, RESOLVER_PERSONAL_TAB_ACCESSIBILITY, RESOLVER_WORK_TAB_ACCESSIBILITY,
+ RESOLVER_CROSS_PROFILE_BLOCKED_TITLE, RESOLVER_CANT_SHARE_WITH_PERSONAL,
+ RESOLVER_CANT_SHARE_WITH_WORK, RESOLVER_CANT_ACCESS_PERSONAL, RESOLVER_CANT_ACCESS_WORK,
+ RESOLVER_WORK_PAUSED_TITLE, RESOLVER_NO_WORK_APPS, RESOLVER_NO_PERSONAL_APPS,
+ CANT_ADD_ACCOUNT_MESSAGE, PACKAGE_INSTALLED_BY_DO, PACKAGE_UPDATED_BY_DO,
+ PACKAGE_DELETED_BY_DO, UNLAUNCHABLE_APP_WORK_PAUSED_TITLE,
+ UNLAUNCHABLE_APP_WORK_PAUSED_MESSAGE, PROFILE_ENCRYPTED_TITLE, PROFILE_ENCRYPTED_DETAIL,
+ PROFILE_ENCRYPTED_MESSAGE, WORK_PROFILE_BADGED_LABEL,
+
+ // DocsUi Strings
+ WORK_PROFILE_OFF_ERROR_TITLE, WORK_PROFILE_OFF_ENABLE_BUTTON,
+ CANT_SELECT_WORK_FILES_TITLE, CANT_SELECT_WORK_FILES_MESSAGE,
+ CANT_SELECT_PERSONAL_FILES_TITLE, CANT_SELECT_PERSONAL_FILES_MESSAGE,
+ CANT_SAVE_TO_WORK_TITLE, CANT_SAVE_TO_WORK_MESSAGE, CANT_SAVE_TO_PERSONAL_TITLE,
+ CANT_SAVE_TO_PERSONAL_MESSAGE, CROSS_PROFILE_NOT_ALLOWED_TITLE,
+ CROSS_PROFILE_NOT_ALLOWED_MESSAGE, PREVIEW_WORK_FILE_ACCESSIBILITY, PERSONAL_TAB,
+ WORK_TAB, WORK_ACCESSIBILITY,
+
+ // MediaProvider Strings
+ SWITCH_TO_WORK_MESSAGE, SWITCH_TO_PERSONAL_MESSAGE, BLOCKED_BY_ADMIN_TITLE,
+ BLOCKED_FROM_PERSONAL_MESSAGE, BLOCKED_FROM_PERSONAL_MESSAGE,
+ BLOCKED_FROM_WORK_MESSAGE, Strings.MediaProvider.WORK_PROFILE_PAUSED_TITLE,
+ WORK_PROFILE_PAUSED_MESSAGE
+ })
+ public @interface UpdatableStringId {
+ }
/**
* Class containing the identifiers used to update device management-related system drawable.
@@ -240,4 +423,995 @@ public final class DevicePolicyResources {
}
}
}
+
+ /**
+ * Class containing the identifiers used to update device management-related system strings.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final class Strings {
+
+ private Strings() {}
+
+ /**
+ * An ID for any string that can't be updated.
+ */
+ public static final String UNDEFINED = "UNDEFINED";
+
+ /**
+ * @hide
+ */
+ public static final Set<String> UPDATABLE_STRING_IDS = buildStringsSet();
+
+ private static Set<String> buildStringsSet() {
+ Set<String> strings = new HashSet<>();
+ strings.addAll(Launcher.buildStringsSet());
+ strings.addAll(SystemUi.buildStringsSet());
+ strings.addAll(Core.buildStringsSet());
+ strings.addAll(DocumentsUi.buildStringsSet());
+ strings.addAll(MediaProvider.buildStringsSet());
+ return strings;
+ }
+
+ /**
+ * Class containing the identifiers used to update device management-related system strings
+ * in the Launcher package.
+ *
+ * @hide
+ */
+ public static final class Launcher {
+
+ private Launcher(){}
+
+ private static final String PREFIX = "Launcher.";
+
+ /**
+ * User on-boarding title for work profile apps.
+ */
+ public static final String WORK_PROFILE_EDU = PREFIX + "WORK_PROFILE_EDU";
+
+ /**
+ * Action label to finish work profile edu.
+ */
+ public static final String WORK_PROFILE_EDU_ACCEPT = PREFIX + "WORK_PROFILE_EDU_ACCEPT";
+
+ /**
+ * Title shown when user opens work apps tab while work profile is paused.
+ */
+ public static final String WORK_PROFILE_PAUSED_TITLE =
+ PREFIX + "WORK_PROFILE_PAUSED_TITLE";
+
+ /**
+ * Description shown when user opens work apps tab while work profile is paused.
+ */
+ public static final String WORK_PROFILE_PAUSED_DESCRIPTION =
+ PREFIX + "WORK_PROFILE_PAUSED_DESCRIPTION";
+
+ /**
+ * Shown on the button to pause work profile.
+ */
+ public static final String WORK_PROFILE_PAUSE_BUTTON =
+ PREFIX + "WORK_PROFILE_PAUSE_BUTTON";
+
+ /**
+ * Shown on the button to enable work profile.
+ */
+ public static final String WORK_PROFILE_ENABLE_BUTTON =
+ PREFIX + "WORK_PROFILE_ENABLE_BUTTON";
+
+ /**
+ * Label on launcher tab to indicate work apps.
+ */
+ public static final String ALL_APPS_WORK_TAB = PREFIX + "ALL_APPS_WORK_TAB";
+
+ /**
+ * Label on launcher tab to indicate personal apps.
+ */
+ public static final String ALL_APPS_PERSONAL_TAB = PREFIX + "ALL_APPS_PERSONAL_TAB";
+
+ /**
+ * Accessibility description for launcher tab to indicate work apps.
+ */
+ public static final String ALL_APPS_WORK_TAB_ACCESSIBILITY =
+ PREFIX + "ALL_APPS_WORK_TAB_ACCESSIBILITY";
+
+ /**
+ * Accessibility description for launcher tab to indicate personal apps.
+ */
+ public static final String ALL_APPS_PERSONAL_TAB_ACCESSIBILITY =
+ PREFIX + "ALL_APPS_PERSONAL_TAB_ACCESSIBILITY";
+
+ /**
+ * Work folder name.
+ */
+ public static final String WORK_FOLDER_NAME = PREFIX + "WORK_FOLDER_NAME";
+
+ /**
+ * Label on widget tab to indicate work app widgets.
+ */
+ public static final String WIDGETS_WORK_TAB = PREFIX + "WIDGETS_WORK_TAB";
+
+ /**
+ * Label on widget tab to indicate personal app widgets.
+ */
+ public static final String WIDGETS_PERSONAL_TAB = PREFIX + "WIDGETS_PERSONAL_TAB";
+
+ /**
+ * Message shown when a feature is disabled by the admin (e.g. changing wallpaper).
+ */
+ public static final String DISABLED_BY_ADMIN_MESSAGE =
+ PREFIX + "DISABLED_BY_ADMIN_MESSAGE";
+
+ /**
+ * @hide
+ */
+ static Set<String> buildStringsSet() {
+ Set<String> strings = new HashSet<>();
+ strings.add(WORK_PROFILE_EDU);
+ strings.add(WORK_PROFILE_EDU_ACCEPT);
+ strings.add(WORK_PROFILE_PAUSED_TITLE);
+ strings.add(WORK_PROFILE_PAUSED_DESCRIPTION);
+ strings.add(WORK_PROFILE_PAUSE_BUTTON);
+ strings.add(WORK_PROFILE_ENABLE_BUTTON);
+ strings.add(ALL_APPS_WORK_TAB);
+ strings.add(ALL_APPS_PERSONAL_TAB);
+ strings.add(ALL_APPS_PERSONAL_TAB_ACCESSIBILITY);
+ strings.add(ALL_APPS_WORK_TAB_ACCESSIBILITY);
+ strings.add(WORK_FOLDER_NAME);
+ strings.add(WIDGETS_WORK_TAB);
+ strings.add(WIDGETS_PERSONAL_TAB);
+ strings.add(DISABLED_BY_ADMIN_MESSAGE);
+ return strings;
+ }
+ }
+
+ /**
+ * Class containing the identifiers used to update device management-related system strings
+ * in the SystemUi package.
+ *
+ * @hide
+ */
+ public static final class SystemUi {
+
+ private SystemUi() {
+ }
+ private static final String PREFIX = "SystemUi.";
+
+ /**
+ * Label in quick settings for toggling work profile on/off.
+ */
+ public static final String QS_WORK_PROFILE_LABEL = PREFIX + "QS_WORK_PROFILE_LABEL";
+
+ /**
+ * Disclosure at the bottom of Quick Settings to indicate device management.
+ */
+ public static final String QS_MSG_MANAGEMENT = PREFIX + "QS_MSG_MANAGEMENT";
+
+ /**
+ * Similar to {@link #QS_MSG_MANAGEMENT} but accepts the organization name as a
+ * param.
+ */
+ public static final String QS_MSG_NAMED_MANAGEMENT = PREFIX + "QS_MSG_NAMED_MANAGEMENT";
+
+ /**
+ * Disclosure at the bottom of Quick Settings to indicate device management monitoring.
+ */
+ public static final String QS_MSG_MANAGEMENT_MONITORING =
+ PREFIX + "QS_MSG_MANAGEMENT_MONITORING";
+
+ /**
+ * Similar to {@link #QS_MSG_MANAGEMENT_MONITORING} but accepts the
+ * organization name as a param.
+ */
+ public static final String QS_MSG_NAMED_MANAGEMENT_MONITORING =
+ PREFIX + "QS_MSG_NAMED_MANAGEMENT_MONITORING";
+
+ /**
+ * Disclosure at the bottom of Quick Settings to indicate device management and the
+ * device is connected to a VPN, accepts VPN name as a param.
+ */
+ public static final String QS_MSG_MANAGEMENT_NAMED_VPN =
+ PREFIX + "QS_MSG_MANAGEMENT_NAMED_VPN";
+
+ /**
+ * Similar to {@link #QS_MSG_MANAGEMENT_NAMED_VPN} but also accepts the
+ * organization name as a param.
+ */
+ public static final String QS_MSG_NAMED_MANAGEMENT_NAMED_VPN =
+ PREFIX + "QS_MSG_NAMED_MANAGEMENT_NAMED_VPN";
+
+ /**
+ * Disclosure at the bottom of Quick Settings to indicate device management and the
+ * device is connected to multiple VPNs.
+ */
+ public static final String QS_MSG_MANAGEMENT_MULTIPLE_VPNS =
+ PREFIX + "QS_MSG_MANAGEMENT_MULTIPLE_VPNS";
+
+ /**
+ * Similar to {@link #QS_MSG_MANAGEMENT_MULTIPLE_VPNS} but also accepts the
+ * organization name as a param.
+ */
+ public static final String QS_MSG_NAMED_MANAGEMENT_MULTIPLE_VPNS =
+ PREFIX + "QS_MSG_NAMED_MANAGEMENT_MULTIPLE_VPNS";
+
+ /**
+ * Disclosure at the bottom of Quick Settings to indicate work profile monitoring.
+ */
+ public static final String QS_MSG_WORK_PROFILE_MONITORING =
+ PREFIX + "QS_MSG_WORK_PROFILE_MONITORING";
+
+ /**
+ * Similar to {@link #QS_MSG_WORK_PROFILE_MONITORING} but accepts the
+ * organization name as a param.
+ */
+ public static final String QS_MSG_NAMED_WORK_PROFILE_MONITORING =
+ PREFIX + "QS_MSG_NAMED_WORK_PROFILE_MONITORING";
+
+ /**
+ * Disclosure at the bottom of Quick Settings to indicate network activity is visible to
+ * admin.
+ */
+ public static final String QS_MSG_WORK_PROFILE_NETWORK =
+ PREFIX + "QS_MSG_WORK_PROFILE_NETWORK";
+
+ /**
+ * Disclosure at the bottom of Quick Settings to indicate work profile is connected to a
+ * VPN, accepts VPN name as a param.
+ */
+ public static final String QS_MSG_WORK_PROFILE_NAMED_VPN =
+ PREFIX + "QS_MSG_WORK_PROFILE_NAMED_VPN";
+
+ /**
+ * Disclosure at the bottom of Quick Settings to indicate personal profile is connected
+ * to a VPN, accepts VPN name as a param.
+ */
+ public static final String QS_MSG_PERSONAL_PROFILE_NAMED_VPN =
+ PREFIX + "QS_MSG_PERSONAL_PROFILE_NAMED_VPN";
+
+ /**
+ * Title for dialog to indicate device management.
+ */
+ public static final String QS_DIALOG_MANAGEMENT_TITLE =
+ PREFIX + "QS_DIALOG_MANAGEMENT_TITLE";
+
+ /**
+ * Label for button in the device management dialog to open a page with more information
+ * on the admin's abilities.
+ */
+ public static final String QS_DIALOG_VIEW_POLICIES =
+ PREFIX + "QS_DIALOG_VIEW_POLICIES";
+
+ /**
+ * Description for device management dialog to indicate admin abilities.
+ */
+ public static final String QS_DIALOG_MANAGEMENT = PREFIX + "QS_DIALOG_MANAGEMENT";
+
+ /**
+ * Similar to {@link #QS_DIALOG_MANAGEMENT} but accepts the organization name as a
+ * param.
+ */
+ public static final String QS_DIALOG_NAMED_MANAGEMENT =
+ PREFIX + "QS_DIALOG_NAMED_MANAGEMENT";
+
+ /**
+ * Description for the managed device certificate authorities in the device management
+ * dialog.
+ */
+ public static final String QS_DIALOG_MANAGEMENT_CA_CERT =
+ PREFIX + "QS_DIALOG_MANAGEMENT_CA_CERT";
+
+ /**
+ * Description for the work profile certificate authorities in the device management
+ * dialog.
+ */
+ public static final String QS_DIALOG_WORK_PROFILE_CA_CERT =
+ PREFIX + "QS_DIALOG_WORK_PROFILE_CA_CERT";
+
+ /**
+ * Description for the managed device network logging in the device management dialog.
+ */
+ public static final String QS_DIALOG_MANAGEMENT_NETWORK =
+ PREFIX + "QS_DIALOG_MANAGEMENT_NETWORK";
+
+ /**
+ * Description for the work profile network logging in the device management dialog.
+ */
+ public static final String QS_DIALOG_WORK_PROFILE_NETWORK =
+ PREFIX + "QS_DIALOG_WORK_PROFILE_NETWORK";
+
+ /**
+ * Description for an active VPN in the device management dialog, accepts VPN name as a
+ * param.
+ */
+ public static final String QS_DIALOG_MANAGEMENT_NAMED_VPN =
+ PREFIX + "QS_DIALOG_MANAGEMENT_NAMED_VPN";
+
+ /**
+ * Description for two active VPN in the device management dialog, accepts two VPN names
+ * as params.
+ */
+ public static final String QS_DIALOG_MANAGEMENT_TWO_NAMED_VPN =
+ PREFIX + "QS_DIALOG_MANAGEMENT_TWO_NAMED_VPN";
+
+ /**
+ * Description for an active work profile VPN in the device management dialog, accepts
+ * VPN name as a param.
+ */
+ public static final String QS_DIALOG_WORK_PROFILE_NAMED_VPN =
+ PREFIX + "QS_DIALOG_WORK_PROFILE_NAMED_VPN";
+
+ /**
+ * Description for an active personal profile VPN in the device management dialog,
+ * accepts VPN name as a param.
+ */
+ public static final String QS_DIALOG_PERSONAL_PROFILE_NAMED_VPN =
+ PREFIX + "QS_DIALOG_PERSONAL_PROFILE_NAMED_VPN";
+
+ /**
+ * Content of a dialog shown when the user only has one attempt left to provide the
+ * correct pin before the work profile is removed.
+ */
+ public static final String BIOMETRIC_DIALOG_WORK_PIN_LAST_ATTEMPT =
+ PREFIX + "BIOMETRIC_DIALOG_WORK_PIN_LAST_ATTEMPT";
+
+ /**
+ * Content of a dialog shown when the user only has one attempt left to provide the
+ * correct pattern before the work profile is removed.
+ */
+ public static final String BIOMETRIC_DIALOG_WORK_PATTERN_LAST_ATTEMPT =
+ PREFIX + "BIOMETRIC_DIALOG_WORK_PATTERN_LAST_ATTEMPT";
+
+ /**
+ * Content of a dialog shown when the user only has one attempt left to provide the
+ * correct password before the work profile is removed.
+ */
+ public static final String BIOMETRIC_DIALOG_WORK_PASSWORD_LAST_ATTEMPT =
+ PREFIX + "BIOMETRIC_DIALOG_WORK_PASSWORD_LAST_ATTEMPT";
+
+ /**
+ * Content of a dialog shown when the user has failed to provide the work lock too many
+ * times and the work profile is removed.
+ */
+ public static final String BIOMETRIC_DIALOG_WORK_LOCK_FAILED_ATTEMPTS =
+ PREFIX + "BIOMETRIC_DIALOG_WORK_LOCK_FAILED_ATTEMPTS";
+
+ /**
+ * Accessibility label for managed profile icon in the status bar
+ */
+ public static final String STATUS_BAR_WORK_ICON_ACCESSIBILITY =
+ PREFIX + "STATUS_BAR_WORK_ICON_ACCESSIBILITY";
+
+ /**
+ * Text appended to privacy dialog, indicating that the application is in the work
+ * profile.
+ */
+ public static final String ONGOING_PRIVACY_DIALOG_WORK =
+ PREFIX + "ONGOING_PRIVACY_DIALOG_WORK";
+
+ /**
+ * Text on keyguard screen indicating device management.
+ */
+ public static final String KEYGUARD_MANAGEMENT_DISCLOSURE =
+ PREFIX + "KEYGUARD_MANAGEMENT_DISCLOSURE";
+
+ /**
+ * Similar to {@link #KEYGUARD_MANAGEMENT_DISCLOSURE} but also accepts organization name
+ * as a param.
+ */
+ public static final String KEYGUARD_NAMED_MANAGEMENT_DISCLOSURE =
+ PREFIX + "KEYGUARD_NAMED_MANAGEMENT_DISCLOSURE";
+
+ /**
+ * Content description for the work profile lock screen.
+ */
+ public static final String WORK_LOCK_ACCESSIBILITY = PREFIX + "WORK_LOCK_ACCESSIBILITY";
+
+ /**
+ * @hide
+ */
+ static Set<String> buildStringsSet() {
+ Set<String> strings = new HashSet<>();
+ strings.add(QS_WORK_PROFILE_LABEL);
+ strings.add(QS_MSG_MANAGEMENT);
+ strings.add(QS_MSG_NAMED_MANAGEMENT);
+ strings.add(QS_MSG_MANAGEMENT_MONITORING);
+ strings.add(QS_MSG_NAMED_MANAGEMENT_MONITORING);
+ strings.add(QS_MSG_MANAGEMENT_NAMED_VPN);
+ strings.add(QS_MSG_NAMED_MANAGEMENT_NAMED_VPN);
+ strings.add(QS_MSG_MANAGEMENT_MULTIPLE_VPNS);
+ strings.add(QS_MSG_NAMED_MANAGEMENT_MULTIPLE_VPNS);
+ strings.add(QS_MSG_WORK_PROFILE_MONITORING);
+ strings.add(QS_MSG_NAMED_WORK_PROFILE_MONITORING);
+ strings.add(QS_MSG_WORK_PROFILE_NETWORK);
+ strings.add(QS_MSG_WORK_PROFILE_NAMED_VPN);
+ strings.add(QS_MSG_PERSONAL_PROFILE_NAMED_VPN);
+ strings.add(QS_DIALOG_MANAGEMENT_TITLE);
+ strings.add(QS_DIALOG_VIEW_POLICIES);
+ strings.add(QS_DIALOG_MANAGEMENT);
+ strings.add(QS_DIALOG_NAMED_MANAGEMENT);
+ strings.add(QS_DIALOG_MANAGEMENT_CA_CERT);
+ strings.add(QS_DIALOG_WORK_PROFILE_CA_CERT);
+ strings.add(QS_DIALOG_MANAGEMENT_NETWORK);
+ strings.add(QS_DIALOG_WORK_PROFILE_NETWORK);
+ strings.add(QS_DIALOG_MANAGEMENT_NAMED_VPN);
+ strings.add(QS_DIALOG_MANAGEMENT_TWO_NAMED_VPN);
+ strings.add(QS_DIALOG_WORK_PROFILE_NAMED_VPN);
+ strings.add(QS_DIALOG_PERSONAL_PROFILE_NAMED_VPN);
+ strings.add(BIOMETRIC_DIALOG_WORK_PIN_LAST_ATTEMPT);
+ strings.add(BIOMETRIC_DIALOG_WORK_PATTERN_LAST_ATTEMPT);
+ strings.add(BIOMETRIC_DIALOG_WORK_PASSWORD_LAST_ATTEMPT);
+ strings.add(BIOMETRIC_DIALOG_WORK_LOCK_FAILED_ATTEMPTS);
+ strings.add(STATUS_BAR_WORK_ICON_ACCESSIBILITY);
+ strings.add(ONGOING_PRIVACY_DIALOG_WORK);
+ strings.add(KEYGUARD_MANAGEMENT_DISCLOSURE);
+ strings.add(KEYGUARD_NAMED_MANAGEMENT_DISCLOSURE);
+ strings.add(WORK_LOCK_ACCESSIBILITY);
+ return strings;
+ }
+ }
+
+ /**
+ * Class containing the identifiers used to update device management-related system strings
+ * in the android core package.
+ *
+ * @hide
+ */
+ public static final class Core {
+
+ private Core() {
+ }
+
+ private static final String PREFIX = "Core.";
+ /**
+ * Notification title when the system deletes the work profile.
+ */
+ public static final String WORK_PROFILE_DELETED_TITLE =
+ PREFIX + "WORK_PROFILE_DELETED_TITLE";
+
+ /**
+ * Content text for the "Work profile deleted" notification to indicates that a work
+ * profile has been deleted because the maximum failed password attempts as been
+ * reached.
+ */
+ public static final String WORK_PROFILE_DELETED_FAILED_PASSWORD_ATTEMPTS_MESSAGE =
+ PREFIX + "WORK_PROFILE_DELETED_FAILED_PASSWORD_ATTEMPTS_MESSAGE";
+
+ /**
+ * Content text for the "Work profile deleted" notification to indicate that a work
+ * profile has been deleted.
+ */
+ public static final String WORK_PROFILE_DELETED_GENERIC_MESSAGE =
+ PREFIX + "WORK_PROFILE_DELETED_GENERIC_MESSAGE";
+
+ /**
+ * Content text for the "Work profile deleted" notification to indicates that a work
+ * profile has been deleted because the admin of an organization-owned device has
+ * relinquishes it.
+ */
+ public static final String WORK_PROFILE_DELETED_ORG_OWNED_MESSAGE =
+ PREFIX + "WORK_PROFILE_DELETED_ORG_OWNED_MESSAGE";
+
+ /**
+ * Notification title for when personal apps are either blocked or will be blocked
+ * soon due to a work policy from their admin.
+ */
+ public static final String PERSONAL_APP_SUSPENSION_TITLE =
+ PREFIX + "PERSONAL_APP_SUSPENSION_TITLE";
+
+ /**
+ * Content text for the personal app suspension notification to indicate that personal
+ * apps are blocked due to a work policy from the admin.
+ */
+ public static final String PERSONAL_APP_SUSPENSION_MESSAGE =
+ PREFIX + "PERSONAL_APP_SUSPENSION_MESSAGE";
+
+ /**
+ * Content text for the personal app suspension notification to indicate that personal
+ * apps will be blocked at a particular time due to a work policy from their admin.
+ * It also explains for how many days the profile is allowed to be off.
+ * <ul>Takes in the following as params:
+ * <li> The date that the personal apps will get suspended at</li>
+ * <li> The time that the personal apps will get suspended at</li>
+ * <li> The max allowed days for the work profile stay switched off</li>
+ * </ul>
+ */
+ public static final String PERSONAL_APP_SUSPENSION_SOON_MESSAGE =
+ PREFIX + "PERSONAL_APP_SUSPENSION_SOON_MESSAGE";
+
+ /**
+ * Title for the button that turns work profile in the personal app suspension
+ * notification.
+ */
+ public static final String PERSONAL_APP_SUSPENSION_TURN_ON_PROFILE =
+ PREFIX + "PERSONAL_APP_SUSPENSION_TURN_ON_PROFILE";
+
+ /**
+ * A toast message displayed when printing is attempted but disabled by policy, accepts
+ * admin name as a param.
+ */
+ public static final String PRINTING_DISABLED_NAMED_ADMIN =
+ PREFIX + "PRINTING_DISABLED_NAMED_ADMIN";
+
+ /**
+ * Notification title to indicate that the device owner has changed the location
+ * settings.
+ */
+ public static final String LOCATION_CHANGED_TITLE = PREFIX + "LOCATION_CHANGED_TITLE";
+
+ /**
+ * Content text for the location changed notification to indicate that the device owner
+ * has changed the location settings.
+ */
+ public static final String LOCATION_CHANGED_MESSAGE =
+ PREFIX + "LOCATION_CHANGED_MESSAGE";
+
+ /**
+ * Notification title to indicate that the device is managed and network logging was
+ * activated by a device owner.
+ */
+ public static final String NETWORK_LOGGING_TITLE = PREFIX + "NETWORK_LOGGING_TITLE";
+
+ /**
+ * Content text for the network logging notification to indicate that the device is
+ * managed and network logging was activated by a device owner.
+ */
+ public static final String NETWORK_LOGGING_MESSAGE = PREFIX + "NETWORK_LOGGING_MESSAGE";
+
+ /**
+ * Content description of the work profile icon in the notifications.
+ */
+ public static final String NOTIFICATION_WORK_PROFILE_CONTENT_DESCRIPTION =
+ PREFIX + "NOTIFICATION_WORK_PROFILE_CONTENT_DESCRIPTION";
+
+ /**
+ * Notification channel name for high-priority alerts from the user's IT admin for key
+ * updates about the device.
+ */
+ public static final String NOTIFICATION_CHANNEL_DEVICE_ADMIN =
+ PREFIX + "NOTIFICATION_CHANNEL_DEVICE_ADMIN";
+
+ /**
+ * Label returned from
+ * {@link android.content.pm.CrossProfileApps#getProfileSwitchingLabel(UserHandle)}
+ * that calling app can show to user for the semantic of switching to work profile.
+ */
+ public static final String SWITCH_TO_WORK_LABEL = PREFIX + "SWITCH_TO_WORK_LABEL";
+
+ /**
+ * Label returned from
+ * {@link android.content.pm.CrossProfileApps#getProfileSwitchingLabel(UserHandle)}
+ * that calling app can show to user for the semantic of switching to personal profile.
+ */
+ public static final String SWITCH_TO_PERSONAL_LABEL =
+ PREFIX + "SWITCH_TO_PERSONAL_LABEL";
+
+ /**
+ * Message to show when an intent automatically switches users into the work profile.
+ */
+ public static final String FORWARD_INTENT_TO_WORK = PREFIX + "FORWARD_INTENT_TO_WORK";
+
+ /**
+ * Message to show when an intent automatically switches users into the personal
+ * profile.
+ */
+ public static final String FORWARD_INTENT_TO_PERSONAL =
+ PREFIX + "FORWARD_INTENT_TO_PERSONAL";
+
+ /**
+ * Text for the toast that is shown when the user clicks on a launcher that doesn't
+ * support the work profile, takes in the launcher name as a param.
+ */
+ public static final String RESOLVER_WORK_PROFILE_NOT_SUPPORTED =
+ PREFIX + "RESOLVER_WORK_PROFILE_NOT_SUPPORTED";
+
+ /**
+ * Label for the personal tab in the {@link com.android.internal.app.ResolverActivity).
+ */
+ public static final String RESOLVER_PERSONAL_TAB = PREFIX + "RESOLVER_PERSONAL_TAB";
+
+ /**
+ * Label for the work tab in the {@link com.android.internal.app.ResolverActivity).
+ */
+ public static final String RESOLVER_WORK_TAB = PREFIX + "RESOLVER_WORK_TAB";
+
+ /**
+ * Accessibility Label for the personal tab in the
+ * {@link com.android.internal.app.ResolverActivity).
+ */
+ public static final String RESOLVER_PERSONAL_TAB_ACCESSIBILITY =
+ PREFIX + "RESOLVER_PERSONAL_TAB_ACCESSIBILITY";
+
+ /**
+ * Accessibility Label for the work tab in the
+ * {@link com.android.internal.app.ResolverActivity).
+ */
+ public static final String RESOLVER_WORK_TAB_ACCESSIBILITY =
+ PREFIX + "RESOLVER_WORK_TAB_ACCESSIBILITY";
+
+ /**
+ * Title for resolver screen to let the user know that their IT admin doesn't allow
+ * them to share this content across profiles.
+ */
+ public static final String RESOLVER_CROSS_PROFILE_BLOCKED_TITLE =
+ PREFIX + "RESOLVER_CROSS_PROFILE_BLOCKED_TITLE";
+
+ /**
+ * Description for resolver screen to let the user know that their IT admin doesn't
+ * allow them to share this content with apps in their personal profile.
+ */
+ public static final String RESOLVER_CANT_SHARE_WITH_PERSONAL =
+ PREFIX + "RESOLVER_CANT_SHARE_WITH_PERSONAL";
+
+ /**
+ * Description for resolver screen to let the user know that their IT admin doesn't
+ * allow them to share this content with apps in their work profile.
+ */
+ public static final String RESOLVER_CANT_SHARE_WITH_WORK =
+ PREFIX + "RESOLVER_CANT_SHARE_WITH_WORK";
+
+ /**
+ * Description for resolver screen to let the user know that their IT admin doesn't
+ * allow them to open this specific content with an app in their personal profile.
+ */
+ public static final String RESOLVER_CANT_ACCESS_PERSONAL =
+ PREFIX + "RESOLVER_CANT_ACCESS_PERSONAL";
+
+ /**
+ * Description for resolver screen to let the user know that their IT admin doesn't
+ * allow them to open this specific content with an app in their work profile.
+ */
+ public static final String RESOLVER_CANT_ACCESS_WORK =
+ PREFIX + "RESOLVER_CANT_ACCESS_WORK";
+
+ /**
+ * Title for resolver screen to let the user know that they need to turn on work apps
+ * in order to share or open content
+ */
+ public static final String RESOLVER_WORK_PAUSED_TITLE =
+ PREFIX + "RESOLVER_WORK_PAUSED_TITLE";
+
+ /**
+ * Text on resolver screen to let the user know that their current work apps don't
+ * support the specific content.
+ */
+ public static final String RESOLVER_NO_WORK_APPS = PREFIX + "RESOLVER_NO_WORK_APPS";
+
+ /**
+ * Text on resolver screen to let the user know that their current personal apps don't
+ * support the specific content.
+ */
+ public static final String RESOLVER_NO_PERSONAL_APPS =
+ PREFIX + "RESOLVER_NO_PERSONAL_APPS";
+
+ /**
+ * Message informing user that the adding the account is disallowed by an administrator.
+ */
+ public static final String CANT_ADD_ACCOUNT_MESSAGE =
+ PREFIX + "CANT_ADD_ACCOUNT_MESSAGE";
+
+ /**
+ * Notification shown when device owner silently installs a package.
+ */
+ public static final String PACKAGE_INSTALLED_BY_DO = PREFIX + "PACKAGE_INSTALLED_BY_DO";
+
+ /**
+ * Notification shown when device owner silently updates a package.
+ */
+ public static final String PACKAGE_UPDATED_BY_DO = PREFIX + "PACKAGE_UPDATED_BY_DO";
+
+ /**
+ * Notification shown when device owner silently deleted a package.
+ */
+ public static final String PACKAGE_DELETED_BY_DO = PREFIX + "PACKAGE_DELETED_BY_DO";
+
+ /**
+ * Title for dialog shown when user tries to open a work app when the work profile is
+ * turned off, confirming that the user wants to turn on access to their
+ * work apps.
+ */
+ public static final String UNLAUNCHABLE_APP_WORK_PAUSED_TITLE =
+ PREFIX + "UNLAUNCHABLE_APP_WORK_PAUSED_TITLE";
+
+ /**
+ * Text for dialog shown when user tries to open a work app when the work profile is
+ * turned off, confirming that the user wants to turn on access to their
+ * work apps.
+ */
+ public static final String UNLAUNCHABLE_APP_WORK_PAUSED_MESSAGE =
+ PREFIX + "UNLAUNCHABLE_APP_WORK_PAUSED_MESSAGE";
+
+ /**
+ * Notification title shown when work profile is credential encrypted and requires
+ * the user to unlock before it's usable.
+ */
+ public static final String PROFILE_ENCRYPTED_TITLE = PREFIX + "PROFILE_ENCRYPTED_TITLE";
+
+ /**
+ * Notification detail shown when work profile is credential encrypted and requires
+ * the user to unlock before it's usable.
+ */
+ public static final String PROFILE_ENCRYPTED_DETAIL =
+ PREFIX + "PROFILE_ENCRYPTED_DETAIL";
+
+ /**
+ * Notification message shown when work profile is credential encrypted and requires
+ * the user to unlock before it's usable.
+ */
+ public static final String PROFILE_ENCRYPTED_MESSAGE =
+ PREFIX + "PROFILE_ENCRYPTED_MESSAGE";
+
+ /**
+ * Used to badge a string with "Work" for work profile content, e.g. "Work Email".
+ * Accepts the string to badge as an argument.
+ * <p>See {@link android.content.pm.PackageManager#getUserBadgedLabel}</p>
+ */
+ public static final String WORK_PROFILE_BADGED_LABEL =
+ PREFIX + "WORK_PROFILE_BADGED_LABEL";
+
+ /**
+ * @hide
+ */
+ static Set<String> buildStringsSet() {
+ Set<String> strings = new HashSet<>();
+ strings.add(WORK_PROFILE_DELETED_TITLE);
+ strings.add(WORK_PROFILE_DELETED_FAILED_PASSWORD_ATTEMPTS_MESSAGE);
+ strings.add(WORK_PROFILE_DELETED_GENERIC_MESSAGE);
+ strings.add(WORK_PROFILE_DELETED_ORG_OWNED_MESSAGE);
+ strings.add(PERSONAL_APP_SUSPENSION_TITLE);
+ strings.add(PERSONAL_APP_SUSPENSION_MESSAGE);
+ strings.add(PERSONAL_APP_SUSPENSION_SOON_MESSAGE);
+ strings.add(PERSONAL_APP_SUSPENSION_TURN_ON_PROFILE);
+ strings.add(PRINTING_DISABLED_NAMED_ADMIN);
+ strings.add(LOCATION_CHANGED_TITLE);
+ strings.add(LOCATION_CHANGED_MESSAGE);
+ strings.add(NETWORK_LOGGING_TITLE);
+ strings.add(NETWORK_LOGGING_MESSAGE);
+ strings.add(NOTIFICATION_WORK_PROFILE_CONTENT_DESCRIPTION);
+ strings.add(NOTIFICATION_CHANNEL_DEVICE_ADMIN);
+ strings.add(SWITCH_TO_WORK_LABEL);
+ strings.add(SWITCH_TO_PERSONAL_LABEL);
+ strings.add(FORWARD_INTENT_TO_WORK);
+ strings.add(FORWARD_INTENT_TO_PERSONAL);
+ strings.add(RESOLVER_WORK_PROFILE_NOT_SUPPORTED);
+ strings.add(RESOLVER_PERSONAL_TAB);
+ strings.add(RESOLVER_WORK_TAB);
+ strings.add(RESOLVER_PERSONAL_TAB_ACCESSIBILITY);
+ strings.add(RESOLVER_WORK_TAB_ACCESSIBILITY);
+ strings.add(RESOLVER_CROSS_PROFILE_BLOCKED_TITLE);
+ strings.add(RESOLVER_CANT_SHARE_WITH_PERSONAL);
+ strings.add(RESOLVER_CANT_SHARE_WITH_WORK);
+ strings.add(RESOLVER_CANT_ACCESS_PERSONAL);
+ strings.add(RESOLVER_CANT_ACCESS_WORK);
+ strings.add(RESOLVER_WORK_PAUSED_TITLE);
+ strings.add(RESOLVER_NO_WORK_APPS);
+ strings.add(RESOLVER_NO_PERSONAL_APPS);
+ strings.add(CANT_ADD_ACCOUNT_MESSAGE);
+ strings.add(PACKAGE_INSTALLED_BY_DO);
+ strings.add(PACKAGE_UPDATED_BY_DO);
+ strings.add(PACKAGE_DELETED_BY_DO);
+ strings.add(UNLAUNCHABLE_APP_WORK_PAUSED_TITLE);
+ strings.add(UNLAUNCHABLE_APP_WORK_PAUSED_MESSAGE);
+ strings.add(PROFILE_ENCRYPTED_TITLE);
+ strings.add(PROFILE_ENCRYPTED_DETAIL);
+ strings.add(PROFILE_ENCRYPTED_MESSAGE);
+ strings.add(WORK_PROFILE_BADGED_LABEL);
+ return strings;
+ }
+ }
+
+ /**
+ * Class containing the identifiers used to update device management-related system strings
+ * in the DocumentsUi package.
+ */
+ public static final class DocumentsUi {
+
+ private DocumentsUi() {
+ }
+
+ private static final String PREFIX = "DocumentsUi.";
+
+ /**
+ * Title for error message shown when work profile is turned off.
+ */
+ public static final String WORK_PROFILE_OFF_ERROR_TITLE =
+ PREFIX + "WORK_PROFILE_OFF_ERROR_TITLE";
+
+ /**
+ * Button text shown when work profile is turned off.
+ */
+ public static final String WORK_PROFILE_OFF_ENABLE_BUTTON =
+ PREFIX + "WORK_PROFILE_OFF_ENABLE_BUTTON";
+
+ /**
+ * Title for error message shown when a user's IT admin does not allow the user to
+ * select work files from a personal app.
+ */
+ public static final String CANT_SELECT_WORK_FILES_TITLE =
+ PREFIX + "CANT_SELECT_WORK_FILES_TITLE";
+
+ /**
+ * Message shown when a user's IT admin does not allow the user to select work files
+ * from a personal app.
+ */
+ public static final String CANT_SELECT_WORK_FILES_MESSAGE =
+ PREFIX + "CANT_SELECT_WORK_FILES_MESSAGE";
+
+ /**
+ * Title for error message shown when a user's IT admin does not allow the user to
+ * select personal files from a work app.
+ */
+ public static final String CANT_SELECT_PERSONAL_FILES_TITLE =
+ PREFIX + "CANT_SELECT_PERSONAL_FILES_TITLE";
+
+ /**
+ * Message shown when a user's IT admin does not allow the user to select personal files
+ * from a work app.
+ */
+ public static final String CANT_SELECT_PERSONAL_FILES_MESSAGE =
+ PREFIX + "CANT_SELECT_PERSONAL_FILES_MESSAGE";
+
+ /**
+ * Title for error message shown when a user's IT admin does not allow the user to save
+ * files from their personal profile to their work profile.
+ */
+ public static final String CANT_SAVE_TO_WORK_TITLE =
+ PREFIX + "CANT_SAVE_TO_WORK_TITLE";
+
+ /**
+ * Message shown when a user's IT admin does not allow the user to save files from their
+ * personal profile to their work profile.
+ */
+ public static final String CANT_SAVE_TO_WORK_MESSAGE =
+ PREFIX + "CANT_SAVE_TO_WORK_MESSAGE";
+
+ /**
+ * Title for error message shown when a user's IT admin does not allow the user to save
+ * files from their work profile to their personal profile.
+ */
+ public static final String CANT_SAVE_TO_PERSONAL_TITLE =
+ PREFIX + "CANT_SAVE_TO_PERSONAL_TITLE";
+
+ /**
+ * Message shown when a user's IT admin does not allow the user to save files from their
+ * work profile to their personal profile.
+ */
+ public static final String CANT_SAVE_TO_PERSONAL_MESSAGE =
+ PREFIX + "CANT_SAVE_TO_PERSONAL_MESSAGE";
+
+ /**
+ * Title for error message shown when a user tries to do something on their work
+ * device, but that action isn't allowed by their IT admin.
+ */
+ public static final String CROSS_PROFILE_NOT_ALLOWED_TITLE =
+ PREFIX + "CROSS_PROFILE_NOT_ALLOWED_TITLE";
+
+ /**
+ * Message shown when a user tries to do something on their work device, but that action
+ * isn't allowed by their IT admin.
+ */
+ public static final String CROSS_PROFILE_NOT_ALLOWED_MESSAGE =
+ PREFIX + "CROSS_PROFILE_NOT_ALLOWED_MESSAGE";
+
+ /**
+ * Content description text that's spoken by a screen reader for previewing a work file
+ * before opening it. Accepts file name as a param.
+ */
+ public static final String PREVIEW_WORK_FILE_ACCESSIBILITY =
+ PREFIX + "PREVIEW_WORK_FILE_ACCESSIBILITY";
+
+ /**
+ * Label for tab and sidebar to indicate personal content.
+ */
+ public static final String PERSONAL_TAB = PREFIX + "PERSONAL_TAB";
+
+ /**
+ * Label for tab and sidebar tab to indicate work content
+ */
+ public static final String WORK_TAB = PREFIX + "WORK_TAB";
+
+ /**
+ * Accessibility label to indicate the subject(e.g. file/folder) is from work profile.
+ */
+ public static final String WORK_ACCESSIBILITY = PREFIX + "WORK_ACCESSIBILITY";
+
+ /**
+ * @hide
+ */
+ static Set<String> buildStringsSet() {
+ Set<String> strings = new HashSet<>();
+ strings.add(WORK_PROFILE_OFF_ERROR_TITLE);
+ strings.add(WORK_PROFILE_OFF_ENABLE_BUTTON);
+ strings.add(CANT_SELECT_WORK_FILES_TITLE);
+ strings.add(CANT_SELECT_WORK_FILES_MESSAGE);
+ strings.add(CANT_SELECT_PERSONAL_FILES_TITLE);
+ strings.add(CANT_SELECT_PERSONAL_FILES_MESSAGE);
+ strings.add(CANT_SAVE_TO_WORK_TITLE);
+ strings.add(CANT_SAVE_TO_WORK_MESSAGE);
+ strings.add(CANT_SAVE_TO_PERSONAL_TITLE);
+ strings.add(CANT_SAVE_TO_PERSONAL_MESSAGE);
+ strings.add(CROSS_PROFILE_NOT_ALLOWED_TITLE);
+ strings.add(CROSS_PROFILE_NOT_ALLOWED_MESSAGE);
+ strings.add(PREVIEW_WORK_FILE_ACCESSIBILITY);
+ strings.add(PERSONAL_TAB);
+ strings.add(WORK_TAB);
+ strings.add(WORK_ACCESSIBILITY);
+ return strings;
+ }
+ }
+
+ /**
+ * Class containing the identifiers used to update device management-related system strings
+ * in the MediaProvider module.
+ */
+ public static final class MediaProvider {
+
+ private MediaProvider() {
+ }
+
+ private static final String PREFIX = "MediaProvider.";
+
+ /**
+ * The text shown to switch to the work profile in PhotoPicker.
+ */
+ public static final String SWITCH_TO_WORK_MESSAGE =
+ PREFIX + "SWITCH_TO_WORK_MESSAGE";
+
+ /**
+ * The text shown to switch to the personal profile in PhotoPicker.
+ */
+ public static final String SWITCH_TO_PERSONAL_MESSAGE =
+ PREFIX + "SWITCH_TO_PERSONAL_MESSAGE";
+
+ /**
+ * The title for error dialog in PhotoPicker when the admin blocks cross user
+ * interaction for the intent.
+ */
+ public static final String BLOCKED_BY_ADMIN_TITLE =
+ PREFIX + "BLOCKED_BY_ADMIN_TITLE";
+
+ /**
+ * The message for error dialog in PhotoPicker when the admin blocks cross user
+ * interaction from the personal profile.
+ */
+ public static final String BLOCKED_FROM_PERSONAL_MESSAGE =
+ PREFIX + "BLOCKED_FROM_PERSONAL_MESSAGE";
+
+ /**
+ * The message for error dialog in PhotoPicker when the admin blocks cross user
+ * interaction from the work profile.
+ */
+ public static final String BLOCKED_FROM_WORK_MESSAGE =
+ PREFIX + "BLOCKED_FROM_WORK_MESSAGE";
+
+ /**
+ * The title of the error dialog in PhotoPicker when the user tries to switch to work
+ * content, but work profile is off.
+ */
+ public static final String WORK_PROFILE_PAUSED_TITLE =
+ PREFIX + "WORK_PROFILE_PAUSED_TITLE";
+
+ /**
+ * The message of the error dialog in PhotoPicker when the user tries to switch to work
+ * content, but work profile is off.
+ */
+ public static final String WORK_PROFILE_PAUSED_MESSAGE =
+ PREFIX + "WORK_PROFILE_PAUSED_MESSAGE";
+
+ /**
+ * @hide
+ */
+ static Set<String> buildStringsSet() {
+ Set<String> strings = new HashSet<>();
+ strings.add(SWITCH_TO_WORK_MESSAGE);
+ strings.add(SWITCH_TO_PERSONAL_MESSAGE);
+ strings.add(BLOCKED_BY_ADMIN_TITLE);
+ strings.add(BLOCKED_FROM_PERSONAL_MESSAGE);
+ strings.add(BLOCKED_FROM_WORK_MESSAGE);
+ strings.add(WORK_PROFILE_PAUSED_TITLE);
+ strings.add(WORK_PROFILE_PAUSED_MESSAGE);
+ return strings;
+ }
+ }
+ }
}
diff --git a/core/java/android/app/admin/DevicePolicyStringResource.aidl b/core/java/android/app/admin/DevicePolicyStringResource.aidl
new file mode 100644
index 000000000000..13b0b958027b
--- /dev/null
+++ b/core/java/android/app/admin/DevicePolicyStringResource.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.admin;
+
+parcelable DevicePolicyStringResource;
diff --git a/core/java/android/app/admin/DevicePolicyStringResource.java b/core/java/android/app/admin/DevicePolicyStringResource.java
new file mode 100644
index 000000000000..5f09bfdcf331
--- /dev/null
+++ b/core/java/android/app/admin/DevicePolicyStringResource.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.admin;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.StringRes;
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Used to pass in the required information for updating an enterprise string resource using
+ * {@link DevicePolicyManager#setStrings}.
+ *
+ * @hide
+ */
+@SystemApi
+public final class DevicePolicyStringResource implements Parcelable {
+ @NonNull private final @DevicePolicyResources.UpdatableStringId String mStringId;
+ private final @StringRes int mCallingPackageResourceId;
+ @NonNull private ParcelableResource mResource;
+
+ /**
+ * Creates an object containing the required information for updating an enterprise string
+ * resource using {@link DevicePolicyManager#setStrings}.
+ *
+ * <p>It will be used to update the string defined by {@code stringId} to the string with ID
+ * {@code callingPackageResourceId} in the calling package</p>
+ *
+ * @param stringId The ID of the string to update.
+ * @param callingPackageResourceId The ID of the {@link StringRes} in the calling package to
+ * use as an updated resource.
+ *
+ * @throws IllegalStateException if the resource with ID {@code callingPackageResourceId}
+ * doesn't exist in the {@code context} package.
+ */
+ public DevicePolicyStringResource(
+ @NonNull Context context,
+ @NonNull @DevicePolicyResources.UpdatableStringId String stringId,
+ @StringRes int callingPackageResourceId) {
+ this(stringId, callingPackageResourceId, new ParcelableResource(
+ context, callingPackageResourceId, ParcelableResource.RESOURCE_TYPE_STRING));
+ }
+
+ private DevicePolicyStringResource(
+ @NonNull @DevicePolicyResources.UpdatableStringId String stringId,
+ @StringRes int callingPackageResourceId,
+ @NonNull ParcelableResource resource) {
+ Objects.requireNonNull(stringId, "stringId must be provided.");
+ Objects.requireNonNull(resource, "ParcelableResource must be provided.");
+
+ this.mStringId = stringId;
+ this.mCallingPackageResourceId = callingPackageResourceId;
+ this.mResource = resource;
+ }
+
+ /**
+ * Returns the ID of the string to update.
+ */
+ @DevicePolicyResources.UpdatableStringId
+ @NonNull
+ public String getStringId() {
+ return mStringId;
+ }
+
+ /**
+ * Returns the ID of the {@link StringRes} in the calling package to use as an updated
+ * resource.
+ */
+ public int getCallingPackageResourceId() {
+ return mCallingPackageResourceId;
+ }
+
+ /**
+ * Returns the {@link ParcelableResource} of the string.
+ *
+ * @hide
+ */
+ @NonNull
+ public ParcelableResource getResource() {
+ return mResource;
+ }
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ DevicePolicyStringResource other = (DevicePolicyStringResource) o;
+ return mStringId == other.mStringId
+ && mCallingPackageResourceId == other.mCallingPackageResourceId
+ && mResource.equals(other.mResource);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mStringId, mCallingPackageResourceId, mResource);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString(mStringId);
+ dest.writeInt(mCallingPackageResourceId);
+ dest.writeTypedObject(mResource, flags);
+ }
+
+ public static final @NonNull Creator<DevicePolicyStringResource> CREATOR =
+ new Creator<DevicePolicyStringResource>() {
+ @Override
+ public DevicePolicyStringResource createFromParcel(Parcel in) {
+ String stringId = in.readString();
+ int callingPackageResourceId = in.readInt();
+ ParcelableResource resource = in.readTypedObject(ParcelableResource.CREATOR);
+
+ return new DevicePolicyStringResource(stringId, callingPackageResourceId, resource);
+ }
+
+ @Override
+ public DevicePolicyStringResource[] newArray(int size) {
+ return new DevicePolicyStringResource[size];
+ }
+ };
+}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 832008755451..f663c17c7884 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -18,6 +18,7 @@
package android.app.admin;
import android.app.admin.DevicePolicyDrawableResource;
+import android.app.admin.DevicePolicyStringResource;
import android.app.admin.ParcelableResource;
import android.app.admin.NetworkEvent;
import android.app.IApplicationThread;
@@ -532,8 +533,20 @@ interface IDevicePolicyManager {
boolean isUsbDataSignalingEnabledForUser(int userId);
boolean canUsbDataSignalingBeDisabled();
+ void setMinimumRequiredWifiSecurityLevel(int level);
+ int getMinimumRequiredWifiSecurityLevel();
+
+ void setSsidAllowlist(in List<String> ssids);
+ List<String> getSsidAllowlist();
+ void setSsidDenylist(in List<String> ssids);
+ List<String> getSsidDenylist();
+
List<UserHandle> listForegroundAffiliatedUsers();
- void setDrawables(in List<DevicePolicyDrawableResource> resource);
+ void setDrawables(in List<DevicePolicyDrawableResource> drawables);
void resetDrawables(in int[] drawableIds);
ParcelableResource getDrawable(int drawableId, int drawableStyle, int drawableSource);
+
+ void setStrings(in List<DevicePolicyStringResource> strings);
+ void resetStrings(in String[] stringIds);
+ ParcelableResource getString(String stringId);
}
diff --git a/core/java/android/app/admin/ParcelableResource.java b/core/java/android/app/admin/ParcelableResource.java
index e5171622be4a..dba362820b1d 100644
--- a/core/java/android/app/admin/ParcelableResource.java
+++ b/core/java/android/app/admin/ParcelableResource.java
@@ -43,7 +43,7 @@ import java.util.concurrent.Callable;
/**
* Used to store the required information to load a resource that was updated using
- * {@link DevicePolicyManager#setDrawables}.
+ * {@link DevicePolicyManager#setDrawables} and {@link DevicePolicyManager#setStrings}.
*
* @hide
*/
@@ -57,10 +57,13 @@ public final class ParcelableResource implements Parcelable {
private static final String ATTR_RESOURCE_TYPE = "resource-type";
public static final int RESOURCE_TYPE_DRAWABLE = 1;
+ public static final int RESOURCE_TYPE_STRING = 2;
+
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = { "RESOURCE_TYPE_" }, value = {
- RESOURCE_TYPE_DRAWABLE
+ RESOURCE_TYPE_DRAWABLE,
+ RESOURCE_TYPE_STRING
})
public @interface ResourceType {}
@@ -78,13 +81,11 @@ public final class ParcelableResource implements Parcelable {
* resource
* @param resourceId of the resource to use as an updated resource
* @param resourceType see {@link ResourceType}
- * @throws IllegalArgumentException if the given {@code resourceId} doesn't exist in the
- * {@link Context#getResources()} of the given {@code context}
*/
- public ParcelableResource(@NonNull Context context, @AnyRes int resourceId,
- @ResourceType int resourceType) throws IllegalArgumentException {
+ public ParcelableResource(
+ @NonNull Context context, @AnyRes int resourceId, @ResourceType int resourceType)
+ throws IllegalStateException, IllegalArgumentException {
Objects.requireNonNull(context, "context must be provided");
-
verifyResourceExistsInCallingPackage(context, resourceId, resourceType);
this.mResourceId = resourceId;
@@ -108,25 +109,41 @@ public final class ParcelableResource implements Parcelable {
private static void verifyResourceExistsInCallingPackage(
Context context, @AnyRes int resourceId, @ResourceType int resourceType)
- throws IllegalArgumentException {
+ throws IllegalStateException, IllegalArgumentException {
switch (resourceType) {
case RESOURCE_TYPE_DRAWABLE:
if (!hasDrawableInCallingPackage(context, resourceId)) {
- throw new IllegalArgumentException(String.format(
+ throw new IllegalStateException(String.format(
"Drawable with id %d doesn't exist in the calling package %s",
resourceId,
context.getPackageName()));
}
break;
+ case RESOURCE_TYPE_STRING:
+ if (!hasStringInCallingPackage(context, resourceId)) {
+ throw new IllegalStateException(String.format(
+ "String with id %d doesn't exist in the calling package %s",
+ resourceId,
+ context.getPackageName()));
+ }
+ break;
default:
throw new IllegalArgumentException(
- "Unknown ParcelableDevicePolicyResourceType: " + resourceType);
+ "Unknown ResourceType: " + resourceType);
}
}
private static boolean hasDrawableInCallingPackage(Context context, @AnyRes int resourceId) {
try {
- return context.getDrawable(resourceId) != null;
+ return "drawable".equals(context.getResources().getResourceTypeName(resourceId));
+ } catch (Resources.NotFoundException e) {
+ return false;
+ }
+ }
+
+ private static boolean hasStringInCallingPackage(Context context, @AnyRes int resourceId) {
+ try {
+ return "string".equals(context.getResources().getResourceTypeName(resourceId));
} catch (Resources.NotFoundException e) {
return false;
}
@@ -158,7 +175,7 @@ public final class ParcelableResource implements Parcelable {
* <p>Returns the default drawable by calling the {@code defaultDrawableLoader} if the updated
* drawable was not found or could not be loaded.</p>
*/
- @Nullable
+ @NonNull
public Drawable getDrawable(
Context context,
int density,
@@ -175,6 +192,59 @@ public final class ParcelableResource implements Parcelable {
}
}
+ /**
+ * Loads the string with id {@code mResourceId} from {@code mPackageName} using the
+ * configuration returned from {@link Resources#getConfiguration} of the provided
+ * {@code context}.
+ *
+ * <p>Returns the default string by calling {@code defaultStringLoader} if the updated
+ * string was not found or could not be loaded.</p>
+ */
+ @NonNull
+ public String getString(
+ Context context,
+ @NonNull Callable<String> defaultStringLoader) {
+ // TODO(b/203548565): properly handle edge case when the device manager role holder is
+ // unavailable because it's being updated.
+ try {
+ Resources resources = getAppResourcesWithCallersConfiguration(context);
+ verifyResourceName(resources);
+ return resources.getString(mResourceId);
+ } catch (PackageManager.NameNotFoundException | RuntimeException e) {
+ Slog.e(TAG, "Unable to load string resource " + mResourceName, e);
+ return loadDefaultString(defaultStringLoader);
+ }
+ }
+
+ /**
+ * Loads the string with id {@code mResourceId} from {@code mPackageName} using the
+ * configuration returned from {@link Resources#getConfiguration} of the provided
+ * {@code context}.
+ *
+ * <p>Returns the default string by calling {@code defaultStringLoader} if the updated
+ * string was not found or could not be loaded.</p>
+ */
+ @Nullable
+ public String getString(
+ Context context,
+ @NonNull Callable<String> defaultStringLoader,
+ @NonNull Object... formatArgs) {
+ // TODO(b/203548565): properly handle edge case when the device manager role holder is
+ // unavailable because it's being updated.
+ try {
+ Resources resources = getAppResourcesWithCallersConfiguration(context);
+ verifyResourceName(resources);
+ String rawString = resources.getString(mResourceId);
+ return String.format(
+ context.getResources().getConfiguration().getLocales().get(0),
+ rawString,
+ formatArgs);
+ } catch (PackageManager.NameNotFoundException | RuntimeException e) {
+ Slog.e(TAG, "Unable to load string resource " + mResourceName, e);
+ return loadDefaultString(defaultStringLoader);
+ }
+ }
+
private Resources getAppResourcesWithCallersConfiguration(Context context)
throws PackageManager.NameNotFoundException {
PackageManager pm = context.getPackageManager();
@@ -195,15 +265,40 @@ public final class ParcelableResource implements Parcelable {
}
/**
- * returns the {@link Drawable} loaded from calling
- * {@code defaultDrawableLoader}.
+ * returns the {@link Drawable} loaded from calling {@code defaultDrawableLoader}.
*/
- public static Drawable loadDefaultDrawable(
- @NonNull Callable<Drawable> defaultDrawableLoader) {
+ @NonNull
+ public static Drawable loadDefaultDrawable(@NonNull Callable<Drawable> defaultDrawableLoader) {
+ try {
+ Objects.requireNonNull(defaultDrawableLoader, "defaultDrawableLoader can't be null");
+
+ Drawable drawable = defaultDrawableLoader.call();
+ Objects.requireNonNull(drawable, "defaultDrawable can't be null");
+
+ return drawable;
+ } catch (NullPointerException rethrown) {
+ throw rethrown;
+ } catch (Exception e) {
+ throw new RuntimeException("Couldn't load default drawable: ", e);
+ }
+ }
+
+ /**
+ * returns the {@link String} loaded from calling {@code defaultStringLoader}.
+ */
+ @NonNull
+ public static String loadDefaultString(@NonNull Callable<String> defaultStringLoader) {
try {
- return defaultDrawableLoader.call();
+ Objects.requireNonNull(defaultStringLoader, "defaultStringLoader can't be null");
+
+ String string = defaultStringLoader.call();
+ Objects.requireNonNull(string, "defaultString can't be null");
+
+ return string;
+ } catch (NullPointerException rethrown) {
+ throw rethrown;
} catch (Exception e) {
- throw new RuntimeException("Couldn't load default drawable", e);
+ throw new RuntimeException("Couldn't load default string: ", e);
}
}
diff --git a/core/java/android/app/admin/WifiSsidPolicy.java b/core/java/android/app/admin/WifiSsidPolicy.java
new file mode 100644
index 000000000000..37150179cc68
--- /dev/null
+++ b/core/java/android/app/admin/WifiSsidPolicy.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.admin;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.ArraySet;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Set;
+
+/**
+ * Used to indicate the Wi-Fi SSID restriction policy the network must satisfy
+ * in order to be eligible for a connection.
+ *
+ * If the policy type is a denylist, the device may not connect to networks on the denylist.
+ * If the policy type is an allowlist, the device may only connect to networks on the allowlist.
+ * Admin configured networks are not exempt from this restriction.
+ * This policy only prohibits connecting to a restricted network and
+ * does not affect adding a restricted network.
+ * If the current network is present in the denylist or not present in the allowlist,
+ * it will be disconnected.
+ */
+public final class WifiSsidPolicy implements Parcelable {
+ /**
+ * SSID policy type indicator for {@link WifiSsidPolicy}.
+ *
+ * <p> When returned from {@link WifiSsidPolicy#getPolicyType()}, the constant
+ * indicates that the SSID policy type is an allowlist.
+ *
+ * @see #WIFI_SSID_POLICY_TYPE_DENYLIST
+ */
+ public static final int WIFI_SSID_POLICY_TYPE_ALLOWLIST = 0;
+
+ /**
+ * SSID policy type indicator for {@link WifiSsidPolicy}.
+ *
+ * <p> When returned from {@link WifiSsidPolicy#getPolicyType()}, the constant
+ * indicates that the SSID policy type is a denylist.
+ *
+ * @see #WIFI_SSID_POLICY_TYPE_ALLOWLIST
+ */
+ public static final int WIFI_SSID_POLICY_TYPE_DENYLIST = 1;
+
+ /**
+ * Possible SSID policy types
+ *
+ * @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"WIFI_SSID_POLICY_TYPE_"}, value = {
+ WIFI_SSID_POLICY_TYPE_ALLOWLIST,
+ WIFI_SSID_POLICY_TYPE_DENYLIST})
+ public @interface WifiSsidPolicyType {}
+
+ private @WifiSsidPolicyType int mPolicyType;
+ private ArraySet<String> mSsids;
+
+ private WifiSsidPolicy(@WifiSsidPolicyType int policyType, @NonNull Set<String> ssids) {
+ mPolicyType = policyType;
+ mSsids = new ArraySet<>(ssids);
+ }
+
+ private WifiSsidPolicy(Parcel in) {
+ mPolicyType = in.readInt();
+ mSsids = (ArraySet<String>) in.readArraySet(null);
+ }
+ /**
+ * Create the allowlist Wi-Fi SSID Policy.
+ *
+ * @param ssids allowlist of SSIDs in UTF-8 without double quotes format
+ * @throws IllegalArgumentException if the input ssids list is empty
+ */
+ @NonNull
+ public static WifiSsidPolicy createAllowlistPolicy(@NonNull Set<String> ssids) {
+ if (ssids.isEmpty()) {
+ throw new IllegalArgumentException("SSID list cannot be empty");
+ }
+ return new WifiSsidPolicy(WIFI_SSID_POLICY_TYPE_ALLOWLIST, ssids);
+ }
+
+ /**
+ * Create the denylist Wi-Fi SSID Policy.
+ *
+ * @param ssids denylist of SSIDs in UTF-8 without double quotes format
+ * @throws IllegalArgumentException if the input ssids list is empty
+ */
+ @NonNull
+ public static WifiSsidPolicy createDenylistPolicy(@NonNull Set<String> ssids) {
+ if (ssids.isEmpty()) {
+ throw new IllegalArgumentException("SSID list cannot be empty");
+ }
+ return new WifiSsidPolicy(WIFI_SSID_POLICY_TYPE_DENYLIST, ssids);
+ }
+
+ /**
+ * Returns the set of SSIDs in UTF-8 without double quotes format.
+ */
+ @NonNull
+ public Set<String> getSsids() {
+ return mSsids;
+ }
+
+ /**
+ * Returns the policy type.
+ */
+ public @WifiSsidPolicyType int getPolicyType() {
+ return mPolicyType;
+ }
+
+ /**
+ * @see Parcelable.Creator
+ */
+ @NonNull
+ public static final Creator<WifiSsidPolicy> CREATOR = new Creator<WifiSsidPolicy>() {
+ @Override
+ public WifiSsidPolicy createFromParcel(Parcel source) {
+ return new WifiSsidPolicy(source);
+ }
+
+ @Override
+ public WifiSsidPolicy[] newArray(int size) {
+ return new WifiSsidPolicy[size];
+ }
+ };
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mPolicyType);
+ dest.writeArraySet(mSsids);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+}
diff --git a/core/java/android/app/ambientcontext/AmbientContextEvent.aidl b/core/java/android/app/ambientcontext/AmbientContextEvent.aidl
new file mode 100644
index 000000000000..0965b1a3f013
--- /dev/null
+++ b/core/java/android/app/ambientcontext/AmbientContextEvent.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.ambientcontext;
+
+parcelable AmbientContextEvent;
diff --git a/core/java/android/app/ambientcontext/AmbientContextEvent.java b/core/java/android/app/ambientcontext/AmbientContextEvent.java
new file mode 100644
index 000000000000..11e695ad7fad
--- /dev/null
+++ b/core/java/android/app/ambientcontext/AmbientContextEvent.java
@@ -0,0 +1,492 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.ambientcontext;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcelable;
+
+import com.android.internal.util.DataClass;
+import com.android.internal.util.Parcelling;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.time.Instant;
+
+
+/**
+ * Represents a detected ambient event. Each event has a type, start time, end time,
+ * plus some optional data.
+ *
+ * @hide
+ */
+@SystemApi
+@DataClass(
+ genBuilder = true,
+ genConstructor = false,
+ genHiddenConstDefs = true,
+ genParcelable = true,
+ genToString = true
+)
+public final class AmbientContextEvent implements Parcelable {
+ /**
+ * The integer indicating an unknown event was detected.
+ */
+ public static final int EVENT_UNKNOWN = 0;
+
+ /**
+ * The integer indicating a cough event was detected.
+ */
+ public static final int EVENT_COUGH = 1;
+
+ /**
+ * The integer indicating a snore event was detected.
+ */
+ public static final int EVENT_SNORE = 2;
+
+ /** @hide */
+ @IntDef(prefix = { "EVENT_" }, value = {
+ EVENT_UNKNOWN,
+ EVENT_COUGH,
+ EVENT_SNORE,
+ }) public @interface EventCode {}
+
+ /** The integer indicating an unknown level. */
+ public static final int LEVEL_UNKNOWN = 0;
+
+ /** The integer indicating a low level. */
+ public static final int LEVEL_LOW = 1;
+
+ /** The integer indicating a medium low level. */
+ public static final int LEVEL_MEDIUM_LOW = 2;
+
+ /** The integer indicating a medium Level. */
+ public static final int LEVEL_MEDIUM = 3;
+
+ /** The integer indicating a medium high level. */
+ public static final int LEVEL_MEDIUM_HIGH = 4;
+
+ /** The integer indicating a high level. */
+ public static final int LEVEL_HIGH = 5;
+
+ /** @hide */
+ @IntDef(prefix = {"LEVEL_"}, value = {
+ LEVEL_UNKNOWN,
+ LEVEL_LOW,
+ LEVEL_MEDIUM_LOW,
+ LEVEL_MEDIUM,
+ LEVEL_MEDIUM_HIGH,
+ LEVEL_HIGH
+ }) public @interface LevelValue {}
+
+ @EventCode private final int mEventType;
+ private static int defaultEventType() {
+ return EVENT_UNKNOWN;
+ }
+
+ /** Event start time */
+ @DataClass.ParcelWith(Parcelling.BuiltIn.ForInstant.class)
+ @NonNull private final Instant mStartTime;
+ @NonNull private static Instant defaultStartTime() {
+ return Instant.MIN;
+ }
+
+ /** Event end time */
+ @DataClass.ParcelWith(Parcelling.BuiltIn.ForInstant.class)
+ @NonNull private final Instant mEndTime;
+ @NonNull private static Instant defaultEndTime() {
+ return Instant.MAX;
+ }
+
+ /**
+ * Confidence level from LEVEL_LOW to LEVEL_HIGH, or LEVEL_NONE if not available.
+ * Apps can add post-processing filter using this value if needed.
+ */
+ @LevelValue private final int mConfidenceLevel;
+ private static int defaultConfidenceLevel() {
+ return LEVEL_UNKNOWN;
+ }
+
+ /**
+ * Density level from LEVEL_LOW to LEVEL_HIGH, or LEVEL_NONE if not available.
+ * Apps can add post-processing filter using this value if needed.
+ */
+ @LevelValue private final int mDensityLevel;
+ private static int defaultDensityLevel() {
+ return LEVEL_UNKNOWN;
+ }
+
+
+
+ // Code below generated by codegen v1.0.23.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/app/ambientcontext/AmbientContextEvent.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ /** @hide */
+ @IntDef(prefix = "EVENT_", value = {
+ EVENT_UNKNOWN,
+ EVENT_COUGH,
+ EVENT_SNORE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @DataClass.Generated.Member
+ public @interface Event {}
+
+ /** @hide */
+ @DataClass.Generated.Member
+ public static String eventToString(@Event int value) {
+ switch (value) {
+ case EVENT_UNKNOWN:
+ return "EVENT_UNKNOWN";
+ case EVENT_COUGH:
+ return "EVENT_COUGH";
+ case EVENT_SNORE:
+ return "EVENT_SNORE";
+ default: return Integer.toHexString(value);
+ }
+ }
+
+ /** @hide */
+ @IntDef(prefix = "LEVEL_", value = {
+ LEVEL_UNKNOWN,
+ LEVEL_LOW,
+ LEVEL_MEDIUM_LOW,
+ LEVEL_MEDIUM,
+ LEVEL_MEDIUM_HIGH,
+ LEVEL_HIGH
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @DataClass.Generated.Member
+ public @interface Level {}
+
+ /** @hide */
+ @DataClass.Generated.Member
+ public static String levelToString(@Level int value) {
+ switch (value) {
+ case LEVEL_UNKNOWN:
+ return "LEVEL_UNKNOWN";
+ case LEVEL_LOW:
+ return "LEVEL_LOW";
+ case LEVEL_MEDIUM_LOW:
+ return "LEVEL_MEDIUM_LOW";
+ case LEVEL_MEDIUM:
+ return "LEVEL_MEDIUM";
+ case LEVEL_MEDIUM_HIGH:
+ return "LEVEL_MEDIUM_HIGH";
+ case LEVEL_HIGH:
+ return "LEVEL_HIGH";
+ default: return Integer.toHexString(value);
+ }
+ }
+
+ @DataClass.Generated.Member
+ /* package-private */ AmbientContextEvent(
+ @EventCode int eventType,
+ @NonNull Instant startTime,
+ @NonNull Instant endTime,
+ @LevelValue int confidenceLevel,
+ @LevelValue int densityLevel) {
+ this.mEventType = eventType;
+ com.android.internal.util.AnnotationValidations.validate(
+ EventCode.class, null, mEventType);
+ this.mStartTime = startTime;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mStartTime);
+ this.mEndTime = endTime;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mEndTime);
+ this.mConfidenceLevel = confidenceLevel;
+ com.android.internal.util.AnnotationValidations.validate(
+ LevelValue.class, null, mConfidenceLevel);
+ this.mDensityLevel = densityLevel;
+ com.android.internal.util.AnnotationValidations.validate(
+ LevelValue.class, null, mDensityLevel);
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public @EventCode int getEventType() {
+ return mEventType;
+ }
+
+ /**
+ * Event start time
+ */
+ @DataClass.Generated.Member
+ public @NonNull Instant getStartTime() {
+ return mStartTime;
+ }
+
+ /**
+ * Event end time
+ */
+ @DataClass.Generated.Member
+ public @NonNull Instant getEndTime() {
+ return mEndTime;
+ }
+
+ /**
+ * Confidence level from LEVEL_LOW to LEVEL_HIGH, or LEVEL_NONE if not available.
+ * Apps can add post-processing filter using this value if needed.
+ */
+ @DataClass.Generated.Member
+ public @LevelValue int getConfidenceLevel() {
+ return mConfidenceLevel;
+ }
+
+ /**
+ * Density level from LEVEL_LOW to LEVEL_HIGH, or LEVEL_NONE if not available.
+ * Apps can add post-processing filter using this value if needed.
+ */
+ @DataClass.Generated.Member
+ public @LevelValue int getDensityLevel() {
+ return mDensityLevel;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public String toString() {
+ // You can override field toString logic by defining methods like:
+ // String fieldNameToString() { ... }
+
+ return "AmbientContextEvent { " +
+ "eventType = " + mEventType + ", " +
+ "startTime = " + mStartTime + ", " +
+ "endTime = " + mEndTime + ", " +
+ "confidenceLevel = " + mConfidenceLevel + ", " +
+ "densityLevel = " + mDensityLevel +
+ " }";
+ }
+
+ @DataClass.Generated.Member
+ static Parcelling<Instant> sParcellingForStartTime =
+ Parcelling.Cache.get(
+ Parcelling.BuiltIn.ForInstant.class);
+ static {
+ if (sParcellingForStartTime == null) {
+ sParcellingForStartTime = Parcelling.Cache.put(
+ new Parcelling.BuiltIn.ForInstant());
+ }
+ }
+
+ @DataClass.Generated.Member
+ static Parcelling<Instant> sParcellingForEndTime =
+ Parcelling.Cache.get(
+ Parcelling.BuiltIn.ForInstant.class);
+ static {
+ if (sParcellingForEndTime == null) {
+ sParcellingForEndTime = Parcelling.Cache.put(
+ new Parcelling.BuiltIn.ForInstant());
+ }
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ dest.writeInt(mEventType);
+ sParcellingForStartTime.parcel(mStartTime, dest, flags);
+ sParcellingForEndTime.parcel(mEndTime, dest, flags);
+ dest.writeInt(mConfidenceLevel);
+ dest.writeInt(mDensityLevel);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ /* package-private */ AmbientContextEvent(@NonNull android.os.Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ int eventType = in.readInt();
+ Instant startTime = sParcellingForStartTime.unparcel(in);
+ Instant endTime = sParcellingForEndTime.unparcel(in);
+ int confidenceLevel = in.readInt();
+ int densityLevel = in.readInt();
+
+ this.mEventType = eventType;
+ com.android.internal.util.AnnotationValidations.validate(
+ EventCode.class, null, mEventType);
+ this.mStartTime = startTime;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mStartTime);
+ this.mEndTime = endTime;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mEndTime);
+ this.mConfidenceLevel = confidenceLevel;
+ com.android.internal.util.AnnotationValidations.validate(
+ LevelValue.class, null, mConfidenceLevel);
+ this.mDensityLevel = densityLevel;
+ com.android.internal.util.AnnotationValidations.validate(
+ LevelValue.class, null, mDensityLevel);
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @NonNull Parcelable.Creator<AmbientContextEvent> CREATOR
+ = new Parcelable.Creator<AmbientContextEvent>() {
+ @Override
+ public AmbientContextEvent[] newArray(int size) {
+ return new AmbientContextEvent[size];
+ }
+
+ @Override
+ public AmbientContextEvent createFromParcel(@NonNull android.os.Parcel in) {
+ return new AmbientContextEvent(in);
+ }
+ };
+
+ /**
+ * A builder for {@link AmbientContextEvent}
+ */
+ @SuppressWarnings("WeakerAccess")
+ @DataClass.Generated.Member
+ public static final class Builder {
+
+ private @EventCode int mEventType;
+ private @NonNull Instant mStartTime;
+ private @NonNull Instant mEndTime;
+ private @LevelValue int mConfidenceLevel;
+ private @LevelValue int mDensityLevel;
+
+ private long mBuilderFieldsSet = 0L;
+
+ public Builder() {
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull Builder setEventType(@EventCode int value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x1;
+ mEventType = value;
+ return this;
+ }
+
+ /**
+ * Event start time
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setStartTime(@NonNull Instant value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x2;
+ mStartTime = value;
+ return this;
+ }
+
+ /**
+ * Event end time
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setEndTime(@NonNull Instant value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x4;
+ mEndTime = value;
+ return this;
+ }
+
+ /**
+ * Confidence level from LEVEL_LOW to LEVEL_HIGH, or LEVEL_NONE if not available.
+ * Apps can add post-processing filter using this value if needed.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setConfidenceLevel(@LevelValue int value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x8;
+ mConfidenceLevel = value;
+ return this;
+ }
+
+ /**
+ * Density level from LEVEL_LOW to LEVEL_HIGH, or LEVEL_NONE if not available.
+ * Apps can add post-processing filter using this value if needed.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setDensityLevel(@LevelValue int value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x10;
+ mDensityLevel = value;
+ return this;
+ }
+
+ /** Builds the instance. This builder should not be touched after calling this! */
+ public @NonNull AmbientContextEvent build() {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x20; // Mark builder used
+
+ if ((mBuilderFieldsSet & 0x1) == 0) {
+ mEventType = defaultEventType();
+ }
+ if ((mBuilderFieldsSet & 0x2) == 0) {
+ mStartTime = defaultStartTime();
+ }
+ if ((mBuilderFieldsSet & 0x4) == 0) {
+ mEndTime = defaultEndTime();
+ }
+ if ((mBuilderFieldsSet & 0x8) == 0) {
+ mConfidenceLevel = defaultConfidenceLevel();
+ }
+ if ((mBuilderFieldsSet & 0x10) == 0) {
+ mDensityLevel = defaultDensityLevel();
+ }
+ AmbientContextEvent o = new AmbientContextEvent(
+ mEventType,
+ mStartTime,
+ mEndTime,
+ mConfidenceLevel,
+ mDensityLevel);
+ return o;
+ }
+
+ private void checkNotUsed() {
+ if ((mBuilderFieldsSet & 0x20) != 0) {
+ throw new IllegalStateException(
+ "This Builder should not be reused. Use a new Builder instance instead");
+ }
+ }
+ }
+
+ @DataClass.Generated(
+ time = 1642040319323L,
+ codegenVersion = "1.0.23",
+ sourceFile = "frameworks/base/core/java/android/app/ambientcontext/AmbientContextEvent.java",
+ inputSignatures = "public static final int EVENT_UNKNOWN\npublic static final int EVENT_COUGH\npublic static final int EVENT_SNORE\npublic static final int LEVEL_UNKNOWN\npublic static final int LEVEL_LOW\npublic static final int LEVEL_MEDIUM_LOW\npublic static final int LEVEL_MEDIUM\npublic static final int LEVEL_MEDIUM_HIGH\npublic static final int LEVEL_HIGH\nprivate final @android.app.ambientcontext.AmbientContextEvent.EventCode int mEventType\nprivate final @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInstant.class) @android.annotation.NonNull java.time.Instant mStartTime\nprivate final @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInstant.class) @android.annotation.NonNull java.time.Instant mEndTime\nprivate final @android.app.ambientcontext.AmbientContextEvent.LevelValue int mConfidenceLevel\nprivate final @android.app.ambientcontext.AmbientContextEvent.LevelValue int mDensityLevel\nprivate static int defaultEventType()\nprivate static @android.annotation.NonNull java.time.Instant defaultStartTime()\nprivate static @android.annotation.NonNull java.time.Instant defaultEndTime()\nprivate static int defaultConfidenceLevel()\nprivate static int defaultDensityLevel()\nclass AmbientContextEvent extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genConstructor=false, genHiddenConstDefs=true, genParcelable=true, genToString=true)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/android/app/ambientcontext/AmbientContextEventRequest.aidl b/core/java/android/app/ambientcontext/AmbientContextEventRequest.aidl
new file mode 100644
index 000000000000..e24c6ad1bcea
--- /dev/null
+++ b/core/java/android/app/ambientcontext/AmbientContextEventRequest.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.ambientcontext;
+
+parcelable AmbientContextEventRequest;
diff --git a/core/java/android/app/ambientcontext/AmbientContextEventRequest.java b/core/java/android/app/ambientcontext/AmbientContextEventRequest.java
new file mode 100644
index 000000000000..82b16a2db0ce
--- /dev/null
+++ b/core/java/android/app/ambientcontext/AmbientContextEventRequest.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.ambientcontext;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.PersistableBundle;
+import android.util.ArraySet;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Represents the request for ambient event detection.
+ *
+ * @hide
+ */
+@SystemApi
+public final class AmbientContextEventRequest implements Parcelable {
+ @NonNull private final Set<Integer> mEventTypes;
+ @NonNull private final PersistableBundle mOptions;
+
+ AmbientContextEventRequest(
+ @NonNull Set<Integer> eventTypes,
+ @NonNull PersistableBundle options) {
+ this.mEventTypes = eventTypes;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mEventTypes);
+ this.mOptions = options;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mOptions);
+ }
+
+ /**
+ * The event types to detect.
+ */
+ public @NonNull Set<Integer> getEventTypes() {
+ return mEventTypes;
+ }
+
+ /**
+ * Optional detection options.
+ */
+ public @NonNull PersistableBundle getOptions() {
+ return mOptions;
+ }
+
+ @Override
+ public String toString() {
+ return "AmbientContextEventRequest { " + "eventTypes = " + mEventTypes + ", "
+ + "options = " + mOptions + " }";
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeArraySet(new ArraySet<>(mEventTypes));
+ dest.writeTypedObject(mOptions, flags);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ AmbientContextEventRequest(@NonNull Parcel in) {
+ Set<Integer> eventTypes = (Set<Integer>) in.readArraySet(Integer.class.getClassLoader());
+ PersistableBundle options = (PersistableBundle) in.readTypedObject(
+ PersistableBundle.CREATOR);
+
+ this.mEventTypes = eventTypes;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mEventTypes);
+ this.mOptions = options;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mOptions);
+ }
+
+ public static final @NonNull Parcelable.Creator<AmbientContextEventRequest> CREATOR =
+ new Parcelable.Creator<AmbientContextEventRequest>() {
+ @Override
+ public AmbientContextEventRequest[] newArray(int size) {
+ return new AmbientContextEventRequest[size];
+ }
+
+ @Override
+ public AmbientContextEventRequest createFromParcel(@NonNull Parcel in) {
+ return new AmbientContextEventRequest(in);
+ }
+ };
+
+ /**
+ * A builder for {@link AmbientContextEventRequest}
+ */
+ @SuppressWarnings("WeakerAccess")
+ public static final class Builder {
+ private @NonNull Set<Integer> mEventTypes;
+ private @NonNull PersistableBundle mOptions;
+
+ private long mBuilderFieldsSet = 0L;
+
+ public Builder() {
+ }
+
+ /**
+ * Add an event type to detect.
+ */
+ public @NonNull Builder addEventType(@AmbientContextEvent.EventCode int value) {
+ checkNotUsed();
+ if (mEventTypes == null) {
+ mBuilderFieldsSet |= 0x1;
+ mEventTypes = new HashSet<>();
+ }
+ mEventTypes.add(value);
+ return this;
+ }
+
+ /**
+ * Optional detection options.
+ */
+ public @NonNull Builder setOptions(@NonNull PersistableBundle value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x2;
+ mOptions = value;
+ return this;
+ }
+
+ /** Builds the instance. This builder should not be touched after calling this! */
+ public @NonNull AmbientContextEventRequest build() {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x4; // Mark builder used
+
+ if ((mBuilderFieldsSet & 0x1) == 0) {
+ mEventTypes = new HashSet<>();
+ }
+ if ((mBuilderFieldsSet & 0x2) == 0) {
+ mOptions = new PersistableBundle();
+ }
+ AmbientContextEventRequest o = new AmbientContextEventRequest(
+ mEventTypes,
+ mOptions);
+ return o;
+ }
+
+ private void checkNotUsed() {
+ if ((mBuilderFieldsSet & 0x4) != 0) {
+ throw new IllegalStateException(
+ "This Builder should not be reused. Use a new Builder instance instead");
+ }
+ }
+ }
+}
diff --git a/core/java/android/app/ambientcontext/AmbientContextEventResponse.aidl b/core/java/android/app/ambientcontext/AmbientContextEventResponse.aidl
new file mode 100644
index 000000000000..4dc6466c7365
--- /dev/null
+++ b/core/java/android/app/ambientcontext/AmbientContextEventResponse.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.ambientcontext;
+
+parcelable AmbientContextEventResponse; \ No newline at end of file
diff --git a/core/java/android/app/ambientcontext/AmbientContextEventResponse.java b/core/java/android/app/ambientcontext/AmbientContextEventResponse.java
new file mode 100644
index 000000000000..472a78b177c9
--- /dev/null
+++ b/core/java/android/app/ambientcontext/AmbientContextEventResponse.java
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.ambientcontext;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.app.PendingIntent;
+import android.os.Parcelable;
+
+import com.android.internal.util.AnnotationValidations;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Represents a response from the {@code AmbientContextEvent} service.
+ *
+ * @hide
+ */
+@SystemApi
+public final class AmbientContextEventResponse implements Parcelable {
+ /**
+ * An unknown status.
+ */
+ public static final int STATUS_UNKNOWN = 0;
+ /**
+ * The value of the status code that indicates success.
+ */
+ public static final int STATUS_SUCCESS = 1;
+ /**
+ * The value of the status code that indicates one or more of the
+ * requested events are not supported.
+ */
+ public static final int STATUS_NOT_SUPPORTED = 2;
+ /**
+ * The value of the status code that indicates service not available.
+ */
+ public static final int STATUS_SERVICE_UNAVAILABLE = 3;
+ /**
+ * The value of the status code that microphone is disabled.
+ */
+ public static final int STATUS_MICROPHONE_DISABLED = 4;
+ /**
+ * The value of the status code that the app is not granted access.
+ */
+ public static final int STATUS_ACCESS_DENIED = 5;
+
+ /** @hide */
+ @IntDef(prefix = { "STATUS_" }, value = {
+ STATUS_UNKNOWN,
+ STATUS_SUCCESS,
+ STATUS_NOT_SUPPORTED,
+ STATUS_SERVICE_UNAVAILABLE,
+ STATUS_MICROPHONE_DISABLED,
+ STATUS_ACCESS_DENIED
+ }) public @interface StatusCode {}
+
+ @StatusCode private final int mStatusCode;
+ @NonNull private final List<AmbientContextEvent> mEvents;
+ @NonNull private final String mPackageName;
+ @Nullable private final PendingIntent mActionPendingIntent;
+
+ /** @hide */
+ public static String statusToString(@StatusCode int value) {
+ switch (value) {
+ case STATUS_UNKNOWN:
+ return "STATUS_UNKNOWN";
+ case STATUS_SUCCESS:
+ return "STATUS_SUCCESS";
+ case STATUS_NOT_SUPPORTED:
+ return "STATUS_NOT_SUPPORTED";
+ case STATUS_SERVICE_UNAVAILABLE:
+ return "STATUS_SERVICE_UNAVAILABLE";
+ case STATUS_MICROPHONE_DISABLED:
+ return "STATUS_MICROPHONE_DISABLED";
+ case STATUS_ACCESS_DENIED:
+ return "STATUS_ACCESS_DENIED";
+ default: return Integer.toHexString(value);
+ }
+ }
+
+ AmbientContextEventResponse(
+ @StatusCode int statusCode,
+ @NonNull List<AmbientContextEvent> events,
+ @NonNull String packageName,
+ @Nullable PendingIntent actionPendingIntent) {
+ this.mStatusCode = statusCode;
+ AnnotationValidations.validate(StatusCode.class, null, mStatusCode);
+ this.mEvents = events;
+ AnnotationValidations.validate(NonNull.class, null, mEvents);
+ this.mPackageName = packageName;
+ AnnotationValidations.validate(NonNull.class, null, mPackageName);
+ this.mActionPendingIntent = actionPendingIntent;
+ }
+
+ /**
+ * The status of the response.
+ */
+ public @StatusCode int getStatusCode() {
+ return mStatusCode;
+ }
+
+ /**
+ * The detected event.
+ */
+ public @NonNull List<AmbientContextEvent> getEvents() {
+ return mEvents;
+ }
+
+ /**
+ * The package to deliver the response to.
+ */
+ public @NonNull String getPackageName() {
+ return mPackageName;
+ }
+
+ /**
+ * A {@link PendingIntent} that the client should call to allow further actions by user.
+ * For example, with {@link STATUS_ACCESS_DENIED}, the PendingIntent can redirect users to the
+ * grant access activity.
+ */
+ public @Nullable PendingIntent getActionPendingIntent() {
+ return mActionPendingIntent;
+ }
+
+ @Override
+ public String toString() {
+ return "AmbientContextEventResponse { " + "statusCode = " + mStatusCode + ", "
+ + "events = " + mEvents + ", " + "packageName = " + mPackageName + ", "
+ + "callbackPendingIntent = " + mActionPendingIntent + " }";
+ }
+
+ @Override
+ public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+ byte flg = 0;
+ if (mActionPendingIntent != null) flg |= 0x8;
+ dest.writeByte(flg);
+ dest.writeInt(mStatusCode);
+ dest.writeParcelableList(mEvents, flags);
+ dest.writeString(mPackageName);
+ if (mActionPendingIntent != null) dest.writeTypedObject(mActionPendingIntent, flags);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ AmbientContextEventResponse(@NonNull android.os.Parcel in) {
+ byte flg = in.readByte();
+ int statusCode = in.readInt();
+ List<AmbientContextEvent> events = new ArrayList<>();
+ in.readParcelableList(events, AmbientContextEvent.class.getClassLoader(),
+ AmbientContextEvent.class);
+ String packageName = in.readString();
+ PendingIntent callbackPendingIntent = (flg & 0x8) == 0 ? null
+ : (PendingIntent) in.readTypedObject(PendingIntent.CREATOR);
+
+ this.mStatusCode = statusCode;
+ AnnotationValidations.validate(
+ StatusCode.class, null, mStatusCode);
+ this.mEvents = events;
+ AnnotationValidations.validate(
+ NonNull.class, null, mEvents);
+ this.mPackageName = packageName;
+ AnnotationValidations.validate(
+ NonNull.class, null, mPackageName);
+ this.mActionPendingIntent = callbackPendingIntent;
+ }
+
+ public static final @NonNull Parcelable.Creator<AmbientContextEventResponse> CREATOR =
+ new Parcelable.Creator<AmbientContextEventResponse>() {
+ @Override
+ public AmbientContextEventResponse[] newArray(int size) {
+ return new AmbientContextEventResponse[size];
+ }
+
+ @Override
+ public AmbientContextEventResponse createFromParcel(@NonNull android.os.Parcel in) {
+ return new AmbientContextEventResponse(in);
+ }
+ };
+
+ /**
+ * A builder for {@link AmbientContextEventResponse}
+ */
+ @SuppressWarnings("WeakerAccess")
+ public static final class Builder {
+ private @StatusCode int mStatusCode;
+ private @NonNull List<AmbientContextEvent> mEvents;
+ private @NonNull String mPackageName;
+ private @Nullable PendingIntent mCallbackPendingIntent;
+ private long mBuilderFieldsSet = 0L;
+
+ public Builder() {
+ }
+
+ /**
+ * The status of the response.
+ */
+ public @NonNull Builder setStatusCode(@StatusCode int value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x1;
+ mStatusCode = value;
+ return this;
+ }
+
+ /**
+ * Adds an event to the builder.
+ */
+ public @NonNull Builder addEvent(@NonNull AmbientContextEvent value) {
+ checkNotUsed();
+ if (mEvents == null) {
+ mBuilderFieldsSet |= 0x2;
+ mEvents = new ArrayList<>();
+ }
+ mEvents.add(value);
+ return this;
+ }
+
+ /**
+ * The package to deliver the response to.
+ */
+ public @NonNull Builder setPackageName(@NonNull String value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x4;
+ mPackageName = value;
+ return this;
+ }
+
+ /**
+ * A {@link PendingIntent} that the client should call to allow further actions by user.
+ * For example, with {@link STATUS_ACCESS_DENIED}, the PendingIntent can redirect users to
+ * the grant access activity.
+ */
+ public @NonNull Builder setActionPendingIntent(@NonNull PendingIntent value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x8;
+ mCallbackPendingIntent = value;
+ return this;
+ }
+
+ /** Builds the instance. This builder should not be touched after calling this! */
+ public @NonNull AmbientContextEventResponse build() {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x10; // Mark builder used
+
+ if ((mBuilderFieldsSet & 0x1) == 0) {
+ mStatusCode = STATUS_UNKNOWN;
+ }
+ if ((mBuilderFieldsSet & 0x2) == 0) {
+ mEvents = new ArrayList<>();
+ }
+ if ((mBuilderFieldsSet & 0x4) == 0) {
+ mPackageName = "";
+ }
+ if ((mBuilderFieldsSet & 0x8) == 0) {
+ mCallbackPendingIntent = null;
+ }
+ AmbientContextEventResponse o = new AmbientContextEventResponse(
+ mStatusCode,
+ mEvents,
+ mPackageName,
+ mCallbackPendingIntent);
+ return o;
+ }
+
+ private void checkNotUsed() {
+ if ((mBuilderFieldsSet & 0x10) != 0) {
+ throw new IllegalStateException(
+ "This Builder should not be reused. Use a new Builder instance instead");
+ }
+ }
+ }
+}
diff --git a/core/java/android/app/ambientcontext/AmbientContextManager.java b/core/java/android/app/ambientcontext/AmbientContextManager.java
new file mode 100644
index 000000000000..6841d1bbfc1f
--- /dev/null
+++ b/core/java/android/app/ambientcontext/AmbientContextManager.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.ambientcontext;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.os.RemoteException;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * Allows granted apps to register for particular pre-defined {@link AmbientContextEvent}s.
+ * After successful registration, the app receives a callback on the provided {@link PendingIntent}
+ * when the requested event is detected.
+ * <p />
+ *
+ * Example:
+ *
+ * <pre><code>
+ * // Create request
+ * AmbientContextEventRequest request = new AmbientContextEventRequest.Builder()
+ * .addEventType(AmbientContextEvent.EVENT_COUGH)
+ * .addEventTYpe(AmbientContextEvent.EVENT_SNORE)
+ * .build();
+ * // Create PendingIntent
+ * Intent intent = new Intent(actionString, null, context, MyBroadcastReceiver.class)
+ * .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ * PendingIntent pendingIntent = PendingIntents.getBroadcastMutable(context, 0, intent, 0);
+ * // Register for events
+ * AmbientContextManager ambientContextManager =
+ * context.getSystemService(AmbientContextManager.class);
+ * ambientContextManager.registerObserver(request, pendingIntent);
+ *
+ * // Handle the callback intent in your receiver
+ * {@literal @}Override
+ * protected void onReceive(Context context, Intent intent) {
+ * AmbientContextEventResponse response =
+ * AmbientContextManager.getResponseFromIntent(intent);
+ * if (response != null) {
+ * if (response.getStatusCode() == AmbientContextEventResponse.STATUS_SUCCESS) {
+ * // Do something useful with response.getEvent()
+ * } else if (response.getStatusCode() == AmbientContextEventResponse.STATUS_ACCESS_DENIED) {
+ * // Redirect users to grant access
+ * PendingIntent callbackPendingIntent = response.getCallbackPendingIntent();
+ * if (callbackPendingIntent != null) {
+ * callbackPendingIntent.send();
+ * }
+ * } else ...
+ * }
+ * }
+ * </code></pre>
+ *
+ * @hide
+ */
+@SystemApi
+@SystemService(Context.AMBIENT_CONTEXT_SERVICE)
+public final class AmbientContextManager {
+
+ /**
+ * The key of an Intent extra indicating the response.
+ */
+ public static final String EXTRA_AMBIENT_CONTEXT_EVENT_RESPONSE =
+ "android.app.ambientcontext.extra.AMBIENT_CONTEXT_EVENT_RESPONSE";
+
+ /**
+ * Allows clients to retrieve the response from the intent.
+ * @param intent received from the PendingIntent callback
+ *
+ * @return the AmbientContextEventResponse, or null if not present
+ */
+ @Nullable
+ public static AmbientContextEventResponse getResponseFromIntent(
+ @NonNull Intent intent) {
+ if (intent.hasExtra(AmbientContextManager.EXTRA_AMBIENT_CONTEXT_EVENT_RESPONSE)) {
+ return intent.getParcelableExtra(EXTRA_AMBIENT_CONTEXT_EVENT_RESPONSE);
+ } else {
+ return null;
+ }
+ }
+
+ private final Context mContext;
+ private final IAmbientContextEventObserver mService;
+
+ /**
+ * {@hide}
+ */
+ public AmbientContextManager(Context context, IAmbientContextEventObserver service) {
+ mContext = context;
+ mService = service;
+ }
+
+ /**
+ * Allows app to register as a {@link AmbientContextEvent} observer. The
+ * observer receives a callback on the provided {@link PendingIntent} when the requested
+ * event is detected. Registering another observer from the same package that has already been
+ * registered will override the previous observer.
+ *
+ * @param request The request with events to observe.
+ * @param pendingIntent A mutable {@link PendingIntent} that will be dispatched when any
+ * requested event is detected.
+ */
+ @RequiresPermission(Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT)
+ public void registerObserver(
+ @NonNull AmbientContextEventRequest request,
+ @NonNull PendingIntent pendingIntent) {
+ Preconditions.checkArgument(!pendingIntent.isImmutable());
+ try {
+ mService.registerObserver(request, pendingIntent);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Unregisters the requesting app as an {@code AmbientContextEvent} observer. Unregistering an
+ * observer that was already unregistered or never registered will have no effect.
+ */
+ @RequiresPermission(Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT)
+ public void unregisterObserver() {
+ try {
+ mService.unregisterObserver(mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+}
diff --git a/core/java/android/app/ambientcontext/IAmbientContextEventObserver.aidl b/core/java/android/app/ambientcontext/IAmbientContextEventObserver.aidl
new file mode 100644
index 000000000000..9032fe1ee045
--- /dev/null
+++ b/core/java/android/app/ambientcontext/IAmbientContextEventObserver.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.ambientcontext;
+
+import android.app.PendingIntent;
+import android.app.ambientcontext.AmbientContextEventRequest;
+
+/**
+ * Interface for an AmbientContextEventManager that provides access to AmbientContextEvents.
+ *
+ * @hide
+ */
+oneway interface IAmbientContextEventObserver {
+ void registerObserver(in AmbientContextEventRequest request, in PendingIntent pendingIntent);
+ void unregisterObserver(in String callingPackage);
+} \ No newline at end of file
diff --git a/core/java/android/app/ambientcontext/OWNERS b/core/java/android/app/ambientcontext/OWNERS
new file mode 100644
index 000000000000..a863297b84e8
--- /dev/null
+++ b/core/java/android/app/ambientcontext/OWNERS
@@ -0,0 +1,3 @@
+enxun@google.com
+kxchen@google.com
+tgadh@google.com
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index e1f6af0cc128..6e49e956fe7e 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -815,13 +815,13 @@ public class AssistStructure implements Parcelable {
mAutofillHints = in.readStringArray();
}
if ((autofillFlags & AUTOFILL_FLAGS_HAS_AUTOFILL_VALUE) != 0) {
- mAutofillValue = in.readParcelable(null);
+ mAutofillValue = in.readParcelable(null, android.view.autofill.AutofillValue.class);
}
if ((autofillFlags & AUTOFILL_FLAGS_HAS_AUTOFILL_OPTIONS) != 0) {
mAutofillOptions = in.readCharSequenceArray();
}
if ((autofillFlags & AUTOFILL_FLAGS_HAS_HTML_INFO) != 0) {
- mHtmlInfo = in.readParcelable(null);
+ mHtmlInfo = in.readParcelable(null, android.view.ViewStructure.HtmlInfo.class);
}
if ((autofillFlags & AUTOFILL_FLAGS_HAS_MIN_TEXT_EMS) != 0) {
mMinEms = in.readInt();
@@ -886,7 +886,7 @@ public class AssistStructure implements Parcelable {
mWebDomain = in.readString();
}
if ((flags&FLAGS_HAS_LOCALE_LIST) != 0) {
- mLocaleList = in.readParcelable(null);
+ mLocaleList = in.readParcelable(null, android.os.LocaleList.class);
}
if ((flags & FLAGS_HAS_MIME_TYPES) != 0) {
mReceiveContentMimeTypes = in.readStringArray();
diff --git a/core/java/android/app/people/ConversationChannel.java b/core/java/android/app/people/ConversationChannel.java
index 2bf71b0183c6..ab350f225e52 100644
--- a/core/java/android/app/people/ConversationChannel.java
+++ b/core/java/android/app/people/ConversationChannel.java
@@ -83,16 +83,16 @@ public final class ConversationChannel implements Parcelable {
}
public ConversationChannel(Parcel in) {
- mShortcutInfo = in.readParcelable(ShortcutInfo.class.getClassLoader());
+ mShortcutInfo = in.readParcelable(ShortcutInfo.class.getClassLoader(), android.content.pm.ShortcutInfo.class);
mUid = in.readInt();
- mNotificationChannel = in.readParcelable(NotificationChannel.class.getClassLoader());
+ mNotificationChannel = in.readParcelable(NotificationChannel.class.getClassLoader(), android.app.NotificationChannel.class);
mNotificationChannelGroup =
- in.readParcelable(NotificationChannelGroup.class.getClassLoader());
+ in.readParcelable(NotificationChannelGroup.class.getClassLoader(), android.app.NotificationChannelGroup.class);
mLastEventTimestamp = in.readLong();
mHasActiveNotifications = in.readBoolean();
mHasBirthdayToday = in.readBoolean();
mStatuses = new ArrayList<>();
- in.readParcelableList(mStatuses, ConversationStatus.class.getClassLoader());
+ in.readParcelableList(mStatuses, ConversationStatus.class.getClassLoader(), android.app.people.ConversationStatus.class);
}
@Override
diff --git a/core/java/android/app/people/ConversationStatus.java b/core/java/android/app/people/ConversationStatus.java
index 8038158b1f97..a7b61b37d14e 100644
--- a/core/java/android/app/people/ConversationStatus.java
+++ b/core/java/android/app/people/ConversationStatus.java
@@ -126,7 +126,7 @@ public final class ConversationStatus implements Parcelable {
mActivity = p.readInt();
mAvailability = p.readInt();
mDescription = p.readCharSequence();
- mIcon = p.readParcelable(Icon.class.getClassLoader());
+ mIcon = p.readParcelable(Icon.class.getClassLoader(), android.graphics.drawable.Icon.class);
mStartTimeMs = p.readLong();
mEndTimeMs = p.readLong();
}
diff --git a/core/java/android/app/people/PeopleSpaceTile.java b/core/java/android/app/people/PeopleSpaceTile.java
index e11861f49be8..4337111636a0 100644
--- a/core/java/android/app/people/PeopleSpaceTile.java
+++ b/core/java/android/app/people/PeopleSpaceTile.java
@@ -472,9 +472,9 @@ public class PeopleSpaceTile implements Parcelable {
public PeopleSpaceTile(Parcel in) {
mId = in.readString();
mUserName = in.readCharSequence();
- mUserIcon = in.readParcelable(Icon.class.getClassLoader());
- mContactUri = in.readParcelable(Uri.class.getClassLoader());
- mUserHandle = in.readParcelable(UserHandle.class.getClassLoader());
+ mUserIcon = in.readParcelable(Icon.class.getClassLoader(), android.graphics.drawable.Icon.class);
+ mContactUri = in.readParcelable(Uri.class.getClassLoader(), android.net.Uri.class);
+ mUserHandle = in.readParcelable(UserHandle.class.getClassLoader(), android.os.UserHandle.class);
mPackageName = in.readString();
mBirthdayText = in.readString();
mLastInteractionTimestamp = in.readLong();
@@ -483,12 +483,12 @@ public class PeopleSpaceTile implements Parcelable {
mNotificationContent = in.readCharSequence();
mNotificationSender = in.readCharSequence();
mNotificationCategory = in.readString();
- mNotificationDataUri = in.readParcelable(Uri.class.getClassLoader());
+ mNotificationDataUri = in.readParcelable(Uri.class.getClassLoader(), android.net.Uri.class);
mMessagesCount = in.readInt();
- mIntent = in.readParcelable(Intent.class.getClassLoader());
+ mIntent = in.readParcelable(Intent.class.getClassLoader(), android.content.Intent.class);
mNotificationTimestamp = in.readLong();
mStatuses = new ArrayList<>();
- in.readParcelableList(mStatuses, ConversationStatus.class.getClassLoader());
+ in.readParcelableList(mStatuses, ConversationStatus.class.getClassLoader(), android.app.people.ConversationStatus.class);
mCanBypassDnd = in.readBoolean();
mIsPackageSuspended = in.readBoolean();
mIsUserQuieted = in.readBoolean();
diff --git a/core/java/android/app/prediction/AppTargetEvent.java b/core/java/android/app/prediction/AppTargetEvent.java
index 963e750e4fd1..51e3953ead4f 100644
--- a/core/java/android/app/prediction/AppTargetEvent.java
+++ b/core/java/android/app/prediction/AppTargetEvent.java
@@ -72,7 +72,7 @@ public final class AppTargetEvent implements Parcelable {
}
private AppTargetEvent(Parcel parcel) {
- mTarget = parcel.readParcelable(null);
+ mTarget = parcel.readParcelable(null, android.app.prediction.AppTarget.class);
mLocation = parcel.readString();
mAction = parcel.readInt();
}
diff --git a/core/java/android/app/servertransaction/ClientTransaction.java b/core/java/android/app/servertransaction/ClientTransaction.java
index fbb37db52014..30a6c311bd1e 100644
--- a/core/java/android/app/servertransaction/ClientTransaction.java
+++ b/core/java/android/app/servertransaction/ClientTransaction.java
@@ -197,11 +197,11 @@ public class ClientTransaction implements Parcelable, ObjectPoolItem {
if (readActivityToken) {
mActivityToken = in.readStrongBinder();
}
- mLifecycleStateRequest = in.readParcelable(getClass().getClassLoader());
+ mLifecycleStateRequest = in.readParcelable(getClass().getClassLoader(), android.app.servertransaction.ActivityLifecycleItem.class);
final boolean readActivityCallbacks = in.readBoolean();
if (readActivityCallbacks) {
mActivityCallbacks = new ArrayList<>();
- in.readParcelableList(mActivityCallbacks, getClass().getClassLoader());
+ in.readParcelableList(mActivityCallbacks, getClass().getClassLoader(), android.app.servertransaction.ClientTransactionItem.class);
}
}
diff --git a/core/java/android/app/smartspace/SmartspaceTargetEvent.java b/core/java/android/app/smartspace/SmartspaceTargetEvent.java
index 61f8723ca393..89caab764591 100644
--- a/core/java/android/app/smartspace/SmartspaceTargetEvent.java
+++ b/core/java/android/app/smartspace/SmartspaceTargetEvent.java
@@ -96,7 +96,7 @@ public final class SmartspaceTargetEvent implements Parcelable {
}
private SmartspaceTargetEvent(Parcel parcel) {
- mSmartspaceTarget = parcel.readParcelable(null);
+ mSmartspaceTarget = parcel.readParcelable(null, android.app.smartspace.SmartspaceTarget.class);
mSmartspaceActionId = parcel.readString();
mEventType = parcel.readInt();
}
diff --git a/core/java/android/app/time/ExternalTimeSuggestion.java b/core/java/android/app/time/ExternalTimeSuggestion.java
index 8e281c07c45d..0f98b4451983 100644
--- a/core/java/android/app/time/ExternalTimeSuggestion.java
+++ b/core/java/android/app/time/ExternalTimeSuggestion.java
@@ -101,11 +101,11 @@ public final class ExternalTimeSuggestion implements Parcelable {
}
private static ExternalTimeSuggestion createFromParcel(Parcel in) {
- TimestampedValue<Long> utcTime = in.readParcelable(null /* classLoader */);
+ TimestampedValue<Long> utcTime = in.readParcelable(null /* classLoader */, android.os.TimestampedValue.class);
ExternalTimeSuggestion suggestion =
new ExternalTimeSuggestion(utcTime.getReferenceTimeMillis(), utcTime.getValue());
@SuppressWarnings("unchecked")
- ArrayList<String> debugInfo = (ArrayList<String>) in.readArrayList(null /* classLoader */);
+ ArrayList<String> debugInfo = (ArrayList<String>) in.readArrayList(null /* classLoader */, java.lang.String.class);
suggestion.mDebugInfo = debugInfo;
return suggestion;
}
diff --git a/core/java/android/app/time/TimeCapabilitiesAndConfig.java b/core/java/android/app/time/TimeCapabilitiesAndConfig.java
index 4a1044760064..71fce14a80b1 100644
--- a/core/java/android/app/time/TimeCapabilitiesAndConfig.java
+++ b/core/java/android/app/time/TimeCapabilitiesAndConfig.java
@@ -59,8 +59,8 @@ public final class TimeCapabilitiesAndConfig implements Parcelable {
@NonNull
private static TimeCapabilitiesAndConfig readFromParcel(Parcel in) {
- TimeCapabilities capabilities = in.readParcelable(null);
- TimeConfiguration configuration = in.readParcelable(null);
+ TimeCapabilities capabilities = in.readParcelable(null, android.app.time.TimeCapabilities.class);
+ TimeConfiguration configuration = in.readParcelable(null, android.app.time.TimeConfiguration.class);
return new TimeCapabilitiesAndConfig(capabilities, configuration);
}
diff --git a/core/java/android/app/time/TimeZoneCapabilitiesAndConfig.java b/core/java/android/app/time/TimeZoneCapabilitiesAndConfig.java
index a9ea76f77958..cd91b0431b28 100644
--- a/core/java/android/app/time/TimeZoneCapabilitiesAndConfig.java
+++ b/core/java/android/app/time/TimeZoneCapabilitiesAndConfig.java
@@ -61,8 +61,8 @@ public final class TimeZoneCapabilitiesAndConfig implements Parcelable {
@NonNull
private static TimeZoneCapabilitiesAndConfig createFromParcel(Parcel in) {
- TimeZoneCapabilities capabilities = in.readParcelable(null);
- TimeZoneConfiguration configuration = in.readParcelable(null);
+ TimeZoneCapabilities capabilities = in.readParcelable(null, android.app.time.TimeZoneCapabilities.class);
+ TimeZoneConfiguration configuration = in.readParcelable(null, android.app.time.TimeZoneConfiguration.class);
return new TimeZoneCapabilitiesAndConfig(capabilities, configuration);
}
diff --git a/core/java/android/app/timedetector/GnssTimeSuggestion.java b/core/java/android/app/timedetector/GnssTimeSuggestion.java
index 6478a2dd2aa9..8ccff6227c79 100644
--- a/core/java/android/app/timedetector/GnssTimeSuggestion.java
+++ b/core/java/android/app/timedetector/GnssTimeSuggestion.java
@@ -66,10 +66,10 @@ public final class GnssTimeSuggestion implements Parcelable {
}
private static GnssTimeSuggestion createFromParcel(Parcel in) {
- TimestampedValue<Long> utcTime = in.readParcelable(null /* classLoader */);
+ TimestampedValue<Long> utcTime = in.readParcelable(null /* classLoader */, android.os.TimestampedValue.class);
GnssTimeSuggestion suggestion = new GnssTimeSuggestion(utcTime);
@SuppressWarnings("unchecked")
- ArrayList<String> debugInfo = (ArrayList<String>) in.readArrayList(null /* classLoader */);
+ ArrayList<String> debugInfo = (ArrayList<String>) in.readArrayList(null /* classLoader */, java.lang.String.class);
suggestion.mDebugInfo = debugInfo;
return suggestion;
}
diff --git a/core/java/android/app/timedetector/ManualTimeSuggestion.java b/core/java/android/app/timedetector/ManualTimeSuggestion.java
index 299e9518e329..1699a5f8c8ae 100644
--- a/core/java/android/app/timedetector/ManualTimeSuggestion.java
+++ b/core/java/android/app/timedetector/ManualTimeSuggestion.java
@@ -66,10 +66,10 @@ public final class ManualTimeSuggestion implements Parcelable {
}
private static ManualTimeSuggestion createFromParcel(Parcel in) {
- TimestampedValue<Long> utcTime = in.readParcelable(null /* classLoader */);
+ TimestampedValue<Long> utcTime = in.readParcelable(null /* classLoader */, android.os.TimestampedValue.class);
ManualTimeSuggestion suggestion = new ManualTimeSuggestion(utcTime);
@SuppressWarnings("unchecked")
- ArrayList<String> debugInfo = (ArrayList<String>) in.readArrayList(null /* classLoader */);
+ ArrayList<String> debugInfo = (ArrayList<String>) in.readArrayList(null /* classLoader */, java.lang.String.class);
suggestion.mDebugInfo = debugInfo;
return suggestion;
}
diff --git a/core/java/android/app/timedetector/NetworkTimeSuggestion.java b/core/java/android/app/timedetector/NetworkTimeSuggestion.java
index a5259c27ec42..20300832d2fc 100644
--- a/core/java/android/app/timedetector/NetworkTimeSuggestion.java
+++ b/core/java/android/app/timedetector/NetworkTimeSuggestion.java
@@ -66,10 +66,10 @@ public final class NetworkTimeSuggestion implements Parcelable {
}
private static NetworkTimeSuggestion createFromParcel(Parcel in) {
- TimestampedValue<Long> utcTime = in.readParcelable(null /* classLoader */);
+ TimestampedValue<Long> utcTime = in.readParcelable(null /* classLoader */, android.os.TimestampedValue.class);
NetworkTimeSuggestion suggestion = new NetworkTimeSuggestion(utcTime);
@SuppressWarnings("unchecked")
- ArrayList<String> debugInfo = (ArrayList<String>) in.readArrayList(null /* classLoader */);
+ ArrayList<String> debugInfo = (ArrayList<String>) in.readArrayList(null /* classLoader */, java.lang.String.class);
suggestion.mDebugInfo = debugInfo;
return suggestion;
}
diff --git a/core/java/android/app/timedetector/TelephonyTimeSuggestion.java b/core/java/android/app/timedetector/TelephonyTimeSuggestion.java
index 6c3a304ed3a7..52d0bbea701e 100644
--- a/core/java/android/app/timedetector/TelephonyTimeSuggestion.java
+++ b/core/java/android/app/timedetector/TelephonyTimeSuggestion.java
@@ -77,10 +77,10 @@ public final class TelephonyTimeSuggestion implements Parcelable {
private static TelephonyTimeSuggestion createFromParcel(Parcel in) {
int slotIndex = in.readInt();
TelephonyTimeSuggestion suggestion = new TelephonyTimeSuggestion.Builder(slotIndex)
- .setUtcTime(in.readParcelable(null /* classLoader */))
+ .setUtcTime(in.readParcelable(null /* classLoader */, android.os.TimestampedValue.class))
.build();
@SuppressWarnings("unchecked")
- ArrayList<String> debugInfo = (ArrayList<String>) in.readArrayList(null /* classLoader */);
+ ArrayList<String> debugInfo = (ArrayList<String>) in.readArrayList(null /* classLoader */, java.lang.String.class);
if (debugInfo != null) {
suggestion.addDebugInfo(debugInfo);
}
diff --git a/core/java/android/app/timezone/RulesState.java b/core/java/android/app/timezone/RulesState.java
index ee88ec54d08f..516ad033a936 100644
--- a/core/java/android/app/timezone/RulesState.java
+++ b/core/java/android/app/timezone/RulesState.java
@@ -195,12 +195,12 @@ public final class RulesState implements Parcelable {
private static RulesState createFromParcel(Parcel in) {
String baseRulesVersion = in.readString();
- DistroFormatVersion distroFormatVersionSupported = in.readParcelable(null);
+ DistroFormatVersion distroFormatVersionSupported = in.readParcelable(null, android.app.timezone.DistroFormatVersion.class);
boolean operationInProgress = in.readByte() == BYTE_TRUE;
int distroStagedState = in.readByte();
- DistroRulesVersion stagedDistroRulesVersion = in.readParcelable(null);
+ DistroRulesVersion stagedDistroRulesVersion = in.readParcelable(null, android.app.timezone.DistroRulesVersion.class);
int installedDistroStatus = in.readByte();
- DistroRulesVersion installedDistroRulesVersion = in.readParcelable(null);
+ DistroRulesVersion installedDistroRulesVersion = in.readParcelable(null, android.app.timezone.DistroRulesVersion.class);
return new RulesState(baseRulesVersion, distroFormatVersionSupported, operationInProgress,
distroStagedState, stagedDistroRulesVersion,
installedDistroStatus, installedDistroRulesVersion);
diff --git a/core/java/android/app/timezonedetector/ManualTimeZoneSuggestion.java b/core/java/android/app/timezonedetector/ManualTimeZoneSuggestion.java
index 01a60b1fa025..387319edc5e7 100644
--- a/core/java/android/app/timezonedetector/ManualTimeZoneSuggestion.java
+++ b/core/java/android/app/timezonedetector/ManualTimeZoneSuggestion.java
@@ -65,7 +65,7 @@ public final class ManualTimeZoneSuggestion implements Parcelable {
String zoneId = in.readString();
ManualTimeZoneSuggestion suggestion = new ManualTimeZoneSuggestion(zoneId);
@SuppressWarnings("unchecked")
- ArrayList<String> debugInfo = (ArrayList<String>) in.readArrayList(null /* classLoader */);
+ ArrayList<String> debugInfo = (ArrayList<String>) in.readArrayList(null /* classLoader */, java.lang.String.class);
suggestion.mDebugInfo = debugInfo;
return suggestion;
}
diff --git a/core/java/android/app/timezonedetector/TelephonyTimeZoneSuggestion.java b/core/java/android/app/timezonedetector/TelephonyTimeZoneSuggestion.java
index eb6750f06d25..e5b4e46ba285 100644
--- a/core/java/android/app/timezonedetector/TelephonyTimeZoneSuggestion.java
+++ b/core/java/android/app/timezonedetector/TelephonyTimeZoneSuggestion.java
@@ -165,7 +165,7 @@ public final class TelephonyTimeZoneSuggestion implements Parcelable {
.setQuality(in.readInt())
.build();
List<String> debugInfo =
- in.readArrayList(TelephonyTimeZoneSuggestion.class.getClassLoader());
+ in.readArrayList(TelephonyTimeZoneSuggestion.class.getClassLoader(), java.lang.String.class);
if (debugInfo != null) {
suggestion.addDebugInfo(debugInfo);
}
diff --git a/core/java/android/app/trust/ITrustListener.aidl b/core/java/android/app/trust/ITrustListener.aidl
index 65b024999a5b..6b9d2c73450e 100644
--- a/core/java/android/app/trust/ITrustListener.aidl
+++ b/core/java/android/app/trust/ITrustListener.aidl
@@ -16,13 +16,16 @@
*/
package android.app.trust;
+import java.util.List;
+
/**
* Private API to be notified about trust changes.
*
* {@hide}
*/
oneway interface ITrustListener {
- void onTrustChanged(boolean enabled, int userId, int flags);
+ void onTrustChanged(boolean enabled, int userId, int flags,
+ in List<String> trustGrantedMessages);
void onTrustManagedChanged(boolean managed, int userId);
void onTrustError(in CharSequence message);
} \ No newline at end of file
diff --git a/core/java/android/app/trust/ITrustManager.aidl b/core/java/android/app/trust/ITrustManager.aidl
index 1291766c00d8..edabccf23c2c 100644
--- a/core/java/android/app/trust/ITrustManager.aidl
+++ b/core/java/android/app/trust/ITrustManager.aidl
@@ -26,6 +26,7 @@ import android.hardware.biometrics.BiometricSourceType;
*/
interface ITrustManager {
void reportUnlockAttempt(boolean successful, int userId);
+ void reportUserRequestedUnlock(int userId);
void reportUnlockLockout(int timeoutMs, int userId);
void reportEnabledTrustAgentsChanged(int userId);
void registerTrustListener(in ITrustListener trustListener);
diff --git a/core/java/android/app/trust/OWNERS b/core/java/android/app/trust/OWNERS
new file mode 100644
index 000000000000..e2c6ce15b51e
--- /dev/null
+++ b/core/java/android/app/trust/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/service/trust/OWNERS
diff --git a/core/java/android/app/trust/TrustManager.java b/core/java/android/app/trust/TrustManager.java
index e214007c1850..70b7de0767e4 100644
--- a/core/java/android/app/trust/TrustManager.java
+++ b/core/java/android/app/trust/TrustManager.java
@@ -29,6 +29,9 @@ import android.os.Message;
import android.os.RemoteException;
import android.util.ArrayMap;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* See {@link com.android.server.trust.TrustManagerService}
* @hide
@@ -43,6 +46,7 @@ public class TrustManager {
private static final String TAG = "TrustManager";
private static final String DATA_FLAGS = "initiatedByUser";
private static final String DATA_MESSAGE = "message";
+ private static final String DATA_GRANTED_MESSAGES = "grantedMessages";
private final ITrustManager mService;
private final ArrayMap<TrustListener, ITrustListener> mTrustListeners;
@@ -85,6 +89,19 @@ public class TrustManager {
}
/**
+ * Reports that the user {@code userId} is likely interested in unlocking the device.
+ *
+ * Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission.
+ */
+ public void reportUserRequestedUnlock(int userId) {
+ try {
+ mService.reportUserRequestedUnlock(userId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Reports that user {@param userId} has entered a temporary device lockout.
*
* This generally occurs when the user has unsuccessfully tried to unlock the device too many
@@ -139,12 +156,15 @@ public class TrustManager {
try {
ITrustListener.Stub iTrustListener = new ITrustListener.Stub() {
@Override
- public void onTrustChanged(boolean enabled, int userId, int flags) {
+ public void onTrustChanged(boolean enabled, int userId, int flags,
+ List<String> trustGrantedMessages) {
Message m = mHandler.obtainMessage(MSG_TRUST_CHANGED, (enabled ? 1 : 0), userId,
trustListener);
if (flags != 0) {
m.getData().putInt(DATA_FLAGS, flags);
}
+ m.getData().putCharSequenceArrayList(
+ DATA_GRANTED_MESSAGES, (ArrayList) trustGrantedMessages);
m.sendToTarget();
}
@@ -231,14 +251,15 @@ public class TrustManager {
switch(msg.what) {
case MSG_TRUST_CHANGED:
int flags = msg.peekData() != null ? msg.peekData().getInt(DATA_FLAGS) : 0;
- ((TrustListener)msg.obj).onTrustChanged(msg.arg1 != 0, msg.arg2, flags);
+ ((TrustListener) msg.obj).onTrustChanged(msg.arg1 != 0, msg.arg2, flags,
+ msg.getData().getStringArrayList(DATA_GRANTED_MESSAGES));
break;
case MSG_TRUST_MANAGED_CHANGED:
((TrustListener)msg.obj).onTrustManagedChanged(msg.arg1 != 0, msg.arg2);
break;
case MSG_TRUST_ERROR:
final CharSequence message = msg.peekData().getCharSequence(DATA_MESSAGE);
- ((TrustListener)msg.obj).onTrustError(message);
+ ((TrustListener) msg.obj).onTrustError(message);
}
}
};
@@ -252,8 +273,11 @@ public class TrustManager {
* @param flags Flags specified by the trust agent when granting trust. See
* {@link android.service.trust.TrustAgentService#grantTrust(CharSequence, long, int)
* TrustAgentService.grantTrust(CharSequence, long, int)}.
+ * @param trustGrantedMessages Messages to display to the user when trust has been granted
+ * by one or more trust agents.
*/
- void onTrustChanged(boolean enabled, int userId, int flags);
+ void onTrustChanged(boolean enabled, int userId, int flags,
+ List<String> trustGrantedMessages);
/**
* Reports that whether trust is managed has changed
diff --git a/core/java/android/app/usage/CacheQuotaHint.java b/core/java/android/app/usage/CacheQuotaHint.java
index 0ccb058d11cf..ba6bcdc936ba 100644
--- a/core/java/android/app/usage/CacheQuotaHint.java
+++ b/core/java/android/app/usage/CacheQuotaHint.java
@@ -148,7 +148,7 @@ public final class CacheQuotaHint implements Parcelable {
return builder.setVolumeUuid(in.readString())
.setUid(in.readInt())
.setQuota(in.readLong())
- .setUsageStats(in.readParcelable(UsageStats.class.getClassLoader()))
+ .setUsageStats(in.readParcelable(UsageStats.class.getClassLoader(), android.app.usage.UsageStats.class))
.build();
}
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index 630b8d26f52e..d7e197ee5ed1 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -1277,6 +1277,28 @@ public final class UsageStatsManager {
}
}
+ /** @hide */
+ public static String standbyBucketToString(int standbyBucket) {
+ switch (standbyBucket) {
+ case STANDBY_BUCKET_EXEMPTED:
+ return "EXEMPTED";
+ case STANDBY_BUCKET_ACTIVE:
+ return "ACTIVE";
+ case STANDBY_BUCKET_WORKING_SET:
+ return "WORKING_SET";
+ case STANDBY_BUCKET_FREQUENT:
+ return "FREQUENT";
+ case STANDBY_BUCKET_RARE:
+ return "RARE";
+ case STANDBY_BUCKET_RESTRICTED:
+ return "RESTRICTED";
+ case STANDBY_BUCKET_NEVER:
+ return "NEVER";
+ default:
+ return String.valueOf(standbyBucket);
+ }
+ }
+
/**
* {@hide}
* Temporarily allowlist the specified app for a short duration. This is to allow an app
diff --git a/core/java/android/app/wallpapereffectsgeneration/OWNERS b/core/java/android/app/wallpapereffectsgeneration/OWNERS
new file mode 100644
index 000000000000..2bc01541a939
--- /dev/null
+++ b/core/java/android/app/wallpapereffectsgeneration/OWNERS
@@ -0,0 +1,5 @@
+susharon@google.com
+shanh@google.com
+huiwu@google.com
+srazdan@google.com
+
diff --git a/core/java/android/companion/AssociationRequest.java b/core/java/android/companion/AssociationRequest.java
index 18a59d863c46..1d2f06d34c8c 100644
--- a/core/java/android/companion/AssociationRequest.java
+++ b/core/java/android/companion/AssociationRequest.java
@@ -595,7 +595,7 @@ public final class AssociationRequest implements Parcelable {
boolean forceConfirmation = (flg & 0x20) != 0;
boolean skipPrompt = (flg & 0x400) != 0;
List<DeviceFilter<?>> deviceFilters = new ArrayList<>();
- in.readParcelableList(deviceFilters, DeviceFilter.class.getClassLoader());
+ in.readParcelableList(deviceFilters, DeviceFilter.class.getClassLoader(), (Class<android.companion.DeviceFilter<?>>) (Class<?>) android.companion.DeviceFilter.class);
String deviceProfile = (flg & 0x4) == 0 ? null : in.readString();
CharSequence displayName = (flg & 0x8) == 0 ? null : (CharSequence) in.readCharSequence();
String packageName = (flg & 0x40) == 0 ? null : in.readString();
diff --git a/core/java/android/companion/BluetoothDeviceFilter.java b/core/java/android/companion/BluetoothDeviceFilter.java
index be663f7bdc1d..e0018f4bad42 100644
--- a/core/java/android/companion/BluetoothDeviceFilter.java
+++ b/core/java/android/companion/BluetoothDeviceFilter.java
@@ -70,7 +70,7 @@ public final class BluetoothDeviceFilter implements DeviceFilter<BluetoothDevice
}
private static List<ParcelUuid> readUuids(Parcel in) {
- return in.readParcelableList(new ArrayList<>(), ParcelUuid.class.getClassLoader());
+ return in.readParcelableList(new ArrayList<>(), ParcelUuid.class.getClassLoader(), android.os.ParcelUuid.class);
}
/** @hide */
diff --git a/core/java/android/companion/BluetoothLeDeviceFilter.java b/core/java/android/companion/BluetoothLeDeviceFilter.java
index 58898cc095be..e6091f04a72a 100644
--- a/core/java/android/companion/BluetoothLeDeviceFilter.java
+++ b/core/java/android/companion/BluetoothLeDeviceFilter.java
@@ -252,7 +252,7 @@ public final class BluetoothLeDeviceFilter implements DeviceFilter<ScanResult> {
public BluetoothLeDeviceFilter createFromParcel(Parcel in) {
Builder builder = new Builder()
.setNamePattern(patternFromString(in.readString()))
- .setScanFilter(in.readParcelable(null));
+ .setScanFilter(in.readParcelable(null, android.bluetooth.le.ScanFilter.class));
byte[] rawDataFilter = in.createByteArray();
byte[] rawDataFilterMask = in.createByteArray();
if (rawDataFilter != null) {
diff --git a/core/java/android/companion/virtual/IVirtualDevice.aidl b/core/java/android/companion/virtual/IVirtualDevice.aidl
index 85855bedfbeb..339e9a2ff1bc 100644
--- a/core/java/android/companion/virtual/IVirtualDevice.aidl
+++ b/core/java/android/companion/virtual/IVirtualDevice.aidl
@@ -18,6 +18,7 @@ package android.companion.virtual;
import android.app.PendingIntent;
import android.graphics.Point;
+import android.graphics.PointF;
import android.hardware.input.VirtualKeyEvent;
import android.hardware.input.VirtualMouseButtonEvent;
import android.hardware.input.VirtualMouseRelativeEvent;
@@ -75,4 +76,5 @@ interface IVirtualDevice {
*/
void launchPendingIntent(
int displayId, in PendingIntent pendingIntent, in ResultReceiver resultReceiver);
+ PointF getCursorPosition(IBinder token);
}
diff --git a/core/java/android/companion/virtual/IVirtualDeviceActivityListener.aidl b/core/java/android/companion/virtual/IVirtualDeviceActivityListener.aidl
new file mode 100644
index 000000000000..53af4c5da761
--- /dev/null
+++ b/core/java/android/companion/virtual/IVirtualDeviceActivityListener.aidl
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.companion.virtual;
+
+import android.content.ComponentName;
+
+/**
+ * Interface to listen for activity changes in a virtual device.
+ *
+ * @hide
+ */
+interface IVirtualDeviceActivityListener {
+
+ /**
+ * Called when the top activity is changed.
+ *
+ * @param displayId The display ID on which the activity change happened.
+ * @param topActivity The component name of the top activity.
+ */
+ void onTopActivityChanged(int displayId, in ComponentName topActivity);
+
+ /**
+ * Called when the display becomes empty (e.g. if the user hits back on the last
+ * activity of the root task).
+ *
+ * @param displayId The display ID that became empty.
+ */
+ void onDisplayEmpty(int displayId);
+}
diff --git a/core/java/android/companion/virtual/IVirtualDeviceManager.aidl b/core/java/android/companion/virtual/IVirtualDeviceManager.aidl
index d80bee668f18..b7f826a940a8 100644
--- a/core/java/android/companion/virtual/IVirtualDeviceManager.aidl
+++ b/core/java/android/companion/virtual/IVirtualDeviceManager.aidl
@@ -17,6 +17,7 @@
package android.companion.virtual;
import android.companion.virtual.IVirtualDevice;
+import android.companion.virtual.IVirtualDeviceActivityListener;
import android.companion.virtual.VirtualDeviceParams;
/**
@@ -39,5 +40,5 @@ interface IVirtualDeviceManager {
*/
IVirtualDevice createVirtualDevice(
in IBinder token, String packageName, int associationId,
- in VirtualDeviceParams params);
+ in VirtualDeviceParams params, in IVirtualDeviceActivityListener activityListener);
}
diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java
index 8ab668873f33..64f16ac3e1b3 100644
--- a/core/java/android/companion/virtual/VirtualDeviceManager.java
+++ b/core/java/android/companion/virtual/VirtualDeviceManager.java
@@ -16,7 +16,6 @@
package android.companion.virtual;
-import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -26,6 +25,7 @@ import android.annotation.SystemService;
import android.app.Activity;
import android.app.PendingIntent;
import android.companion.AssociationInfo;
+import android.content.ComponentName;
import android.content.Context;
import android.graphics.Point;
import android.hardware.display.DisplayManager;
@@ -41,12 +41,9 @@ import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
import android.os.ResultReceiver;
+import android.util.ArrayMap;
import android.view.Surface;
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
import java.util.concurrent.Executor;
/**
@@ -61,23 +58,6 @@ public final class VirtualDeviceManager {
private static final boolean DEBUG = false;
private static final String LOG_TAG = "VirtualDeviceManager";
- /** @hide */
- @IntDef(prefix = "DISPLAY_FLAG_",
- flag = true,
- value = {DISPLAY_FLAG_TRUSTED})
- @Retention(RetentionPolicy.SOURCE)
- @Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
- public @interface DisplayFlags {}
-
- /**
- * Indicates that the display is trusted to show system decorations and receive inputs without
- * users' touch.
- *
- * @see DisplayManager#VIRTUAL_DISPLAY_FLAG_TRUSTED
- * @hide // TODO(b/194949534): Unhide this API
- */
- public static final int DISPLAY_FLAG_TRUSTED = 1;
-
private static final int DEFAULT_VIRTUAL_DISPLAY_FLAGS =
DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC
| DisplayManager.VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT
@@ -102,16 +82,14 @@ public final class VirtualDeviceManager {
* @param associationId The association ID as returned by {@link AssociationInfo#getId()} from
* Companion Device Manager. Virtual devices must have a corresponding association with CDM in
* order to be created.
- * @hide
*/
@RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
@Nullable
- public VirtualDevice createVirtualDevice(int associationId, VirtualDeviceParams params) {
- // TODO(b/194949534): Unhide this API
+ public VirtualDevice createVirtualDevice(
+ int associationId,
+ @NonNull VirtualDeviceParams params) {
try {
- IVirtualDevice virtualDevice = mService.createVirtualDevice(
- new Binder(), mContext.getPackageName(), associationId, params);
- return new VirtualDevice(mContext, virtualDevice);
+ return new VirtualDevice(mService, mContext, associationId, params);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -128,10 +106,49 @@ public final class VirtualDeviceManager {
private final Context mContext;
private final IVirtualDevice mVirtualDevice;
+ private final ArrayMap<ActivityListener, ActivityListenerDelegate> mActivityListeners =
+ new ArrayMap<>();
+ private final IVirtualDeviceActivityListener mActivityListenerBinder =
+ new IVirtualDeviceActivityListener.Stub() {
- private VirtualDevice(Context context, IVirtualDevice virtualDevice) {
+ @Override
+ public void onTopActivityChanged(int displayId, ComponentName topActivity) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ for (int i = 0; i < mActivityListeners.size(); i++) {
+ mActivityListeners.valueAt(i)
+ .onTopActivityChanged(displayId, topActivity);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void onDisplayEmpty(int displayId) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ for (int i = 0; i < mActivityListeners.size(); i++) {
+ mActivityListeners.valueAt(i).onDisplayEmpty(displayId);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ };
+
+ private VirtualDevice(
+ IVirtualDeviceManager service,
+ Context context,
+ int associationId,
+ VirtualDeviceParams params) throws RemoteException {
mContext = context.getApplicationContext();
- mVirtualDevice = virtualDevice;
+ mVirtualDevice = service.createVirtualDevice(
+ new Binder(),
+ mContext.getPackageName(),
+ associationId,
+ params,
+ mActivityListenerBinder);
}
/**
@@ -159,7 +176,7 @@ public final class VirtualDeviceManager {
mVirtualDevice.launchPendingIntent(
displayId,
pendingIntent,
- new ResultReceiver(new Handler(Looper.myLooper())) {
+ new ResultReceiver(new Handler(Looper.getMainLooper())) {
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
super.onReceiveResult(resultCode, resultData);
@@ -179,7 +196,9 @@ public final class VirtualDeviceManager {
/**
* Creates a virtual display for this virtual device. All displays created on the same
- * device belongs to the same display group.
+ * device belongs to the same display group. Requires the ADD_TRUSTED_DISPLAY permission
+ * to create a virtual display which is not in the default DisplayGroup, and to create
+ * trusted displays.
*
* @param width The width of the virtual display in pixels, must be greater than 0.
* @param height The height of the virtual display in pixels, must be greater than 0.
@@ -187,7 +206,12 @@ public final class VirtualDeviceManager {
* @param surface The surface to which the content of the virtual display should
* be rendered, or null if there is none initially. The surface can also be set later using
* {@link VirtualDisplay#setSurface(Surface)}.
- * @param flags Either 0, or {@link #DISPLAY_FLAG_TRUSTED}.
+ * @param flags A combination of virtual display flags accepted by
+ * {@link DisplayManager#createVirtualDisplay}. In addition, the following flags are
+ * automatically set for all virtual devices:
+ * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_PUBLIC VIRTUAL_DISPLAY_FLAG_PUBLIC} and
+ * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY
+ * VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY}.
* @param callback Callback to call when the state of the {@link VirtualDisplay} changes
* @param handler The handler on which the listener should be invoked, or null
* if the listener should be invoked on the calling thread's looper.
@@ -195,9 +219,7 @@ public final class VirtualDeviceManager {
* not create the virtual display.
*
* @see DisplayManager#createVirtualDisplay
- * @hide
*/
- // TODO(b/194949534): Unhide this API
// Suppress "ExecutorRegistration" because DisplayManager.createVirtualDisplay takes a
// handler
@SuppressLint("ExecutorRegistration")
@@ -207,7 +229,7 @@ public final class VirtualDeviceManager {
int height,
int densityDpi,
@Nullable Surface surface,
- @DisplayFlags int flags,
+ int flags,
@Nullable Handler handler,
@Nullable VirtualDisplay.Callback callback) {
// TODO(b/205343547): Handle display groups properly instead of creating a new display
@@ -246,7 +268,6 @@ public final class VirtualDeviceManager {
* @param inputDeviceName the name to call this input device
* @param vendorId the vendor id
* @param productId the product id
- * @hide
*/
@RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
@NonNull
@@ -273,7 +294,6 @@ public final class VirtualDeviceManager {
* @param inputDeviceName the name to call this input device
* @param vendorId the vendor id
* @param productId the product id
- * @hide
*/
@RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
@NonNull
@@ -300,7 +320,6 @@ public final class VirtualDeviceManager {
* @param inputDeviceName the name to call this input device
* @param vendorId the vendor id
* @param productId the product id
- * @hide
*/
@RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
@NonNull
@@ -328,12 +347,8 @@ public final class VirtualDeviceManager {
* com.android.server.companion.virtual.VirtualDeviceImpl#getBaseVirtualDisplayFlags()} will
* be added by DisplayManagerService.
*/
- private int getVirtualDisplayFlags(@DisplayFlags int flags) {
- int virtualDisplayFlags = DEFAULT_VIRTUAL_DISPLAY_FLAGS;
- if ((flags & DISPLAY_FLAG_TRUSTED) != 0) {
- virtualDisplayFlags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_TRUSTED;
- }
- return virtualDisplayFlags;
+ private int getVirtualDisplayFlags(int flags) {
+ return DEFAULT_VIRTUAL_DISPLAY_FLAGS | flags;
}
private String getVirtualDisplayName() {
@@ -347,6 +362,47 @@ public final class VirtualDeviceManager {
throw e.rethrowFromSystemServer();
}
}
+
+ /**
+ * Adds an activity listener to listen for events such as top activity change or virtual
+ * display task stack became empty.
+ *
+ * @param listener The listener to add.
+ * @see #removeActivityListener(ActivityListener)
+ * @hide
+ */
+ // TODO(b/194949534): Unhide this API
+ public void addActivityListener(@NonNull ActivityListener listener) {
+ addActivityListener(listener, mContext.getMainExecutor());
+ }
+
+ /**
+ * Adds an activity listener to listen for events such as top activity change or virtual
+ * display task stack became empty.
+ *
+ * @param listener The listener to add.
+ * @param executor The executor where the callback is executed on.
+ * @see #removeActivityListener(ActivityListener)
+ * @hide
+ */
+ // TODO(b/194949534): Unhide this API
+ public void addActivityListener(
+ @NonNull ActivityListener listener, @NonNull Executor executor) {
+ mActivityListeners.put(listener, new ActivityListenerDelegate(listener, executor));
+ }
+
+ /**
+ * Removes an activity listener previously added with
+ * {@link #addActivityListener}.
+ *
+ * @param listener The listener to remove.
+ * @see #addActivityListener(ActivityListener, Executor)
+ * @hide
+ */
+ // TODO(b/194949534): Unhide this API
+ public void removeActivityListener(@NonNull ActivityListener listener) {
+ mActivityListeners.remove(listener);
+ }
}
/**
@@ -366,4 +422,50 @@ public final class VirtualDeviceManager {
*/
void onLaunchFailed();
}
+
+ /**
+ * Listener for activity changes in this virtual device.
+ *
+ * @hide
+ */
+ // TODO(b/194949534): Unhide this API
+ public interface ActivityListener {
+
+ /**
+ * Called when the top activity is changed.
+ *
+ * @param displayId The display ID on which the activity change happened.
+ * @param topActivity The component name of the top activity.
+ */
+ void onTopActivityChanged(int displayId, @NonNull ComponentName topActivity);
+
+ /**
+ * Called when the display becomes empty (e.g. if the user hits back on the last
+ * activity of the root task).
+ *
+ * @param displayId The display ID that became empty.
+ */
+ void onDisplayEmpty(int displayId);
+ }
+
+ /**
+ * A wrapper for {@link ActivityListener} that executes callbacks on the given executor.
+ */
+ private static class ActivityListenerDelegate {
+ @NonNull private final ActivityListener mActivityListener;
+ @NonNull private final Executor mExecutor;
+
+ ActivityListenerDelegate(@NonNull ActivityListener listener, @NonNull Executor executor) {
+ mActivityListener = listener;
+ mExecutor = executor;
+ }
+
+ public void onTopActivityChanged(int displayId, ComponentName topActivity) {
+ mExecutor.execute(() -> mActivityListener.onTopActivityChanged(displayId, topActivity));
+ }
+
+ public void onDisplayEmpty(int displayId) {
+ mExecutor.execute(() -> mActivityListener.onDisplayEmpty(displayId));
+ }
+ }
}
diff --git a/core/java/android/companion/virtual/VirtualDeviceParams.java b/core/java/android/companion/virtual/VirtualDeviceParams.java
index d61d4741637a..2ddfeb4c8ab5 100644
--- a/core/java/android/companion/virtual/VirtualDeviceParams.java
+++ b/core/java/android/companion/virtual/VirtualDeviceParams.java
@@ -20,7 +20,10 @@ import static android.Manifest.permission.ADD_ALWAYS_UNLOCKED_DISPLAY;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.content.ComponentName;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.UserHandle;
@@ -39,7 +42,7 @@ import java.util.Set;
*
* @hide
*/
-// TODO(b/194949534): Unhide this API
+@SystemApi
public final class VirtualDeviceParams implements Parcelable {
/** @hide */
@@ -51,32 +54,36 @@ public final class VirtualDeviceParams implements Parcelable {
/**
* Indicates that the lock state of the virtual device should be always locked.
- *
- * @hide // TODO(b/194949534): Unhide this API
*/
public static final int LOCK_STATE_ALWAYS_LOCKED = 0;
/**
* Indicates that the lock state of the virtual device should be always unlocked.
- *
- * @hide // TODO(b/194949534): Unhide this API
*/
public static final int LOCK_STATE_ALWAYS_UNLOCKED = 1;
private final int mLockState;
private final ArraySet<UserHandle> mUsersWithMatchingAccounts;
+ @Nullable private final ArraySet<ComponentName> mAllowedActivities;
+ @Nullable private final ArraySet<ComponentName> mBlockedActivities;
private VirtualDeviceParams(
@LockState int lockState,
- @NonNull Set<UserHandle> usersWithMatchingAccounts) {
+ @NonNull Set<UserHandle> usersWithMatchingAccounts,
+ @Nullable Set<ComponentName> allowedActivities,
+ @Nullable Set<ComponentName> blockedActivities) {
mLockState = lockState;
mUsersWithMatchingAccounts = new ArraySet<>(usersWithMatchingAccounts);
+ mAllowedActivities = allowedActivities == null ? null : new ArraySet<>(allowedActivities);
+ mBlockedActivities = blockedActivities == null ? null : new ArraySet<>(blockedActivities);
}
@SuppressWarnings("unchecked")
private VirtualDeviceParams(Parcel parcel) {
mLockState = parcel.readInt();
mUsersWithMatchingAccounts = (ArraySet<UserHandle>) parcel.readArraySet(null);
+ mAllowedActivities = (ArraySet<ComponentName>) parcel.readArraySet(null);
+ mBlockedActivities = (ArraySet<ComponentName>) parcel.readArraySet(null);
}
/**
@@ -98,6 +105,35 @@ public final class VirtualDeviceParams implements Parcelable {
return Collections.unmodifiableSet(mUsersWithMatchingAccounts);
}
+ /**
+ * Returns the set of activities allowed to be streamed, or {@code null} if this is not set.
+ *
+ * @see Builder#setAllowedActivities(Set)
+ * @hide // TODO(b/194949534): Unhide this API
+ */
+ @Nullable
+ public Set<ComponentName> getAllowedActivities() {
+ if (mAllowedActivities == null) {
+ return null;
+ }
+ return Collections.unmodifiableSet(mAllowedActivities);
+ }
+
+ /**
+ * Returns the set of activities that are blocked from streaming, or {@code null} if this is not
+ * set.
+ *
+ * @see Builder#setBlockedActivities(Set)
+ * @hide // TODO(b/194949534): Unhide this API
+ */
+ @Nullable
+ public Set<ComponentName> getBlockedActivities() {
+ if (mBlockedActivities == null) {
+ return null;
+ }
+ return Collections.unmodifiableSet(mBlockedActivities);
+ }
+
@Override
public int describeContents() {
return 0;
@@ -107,6 +143,8 @@ public final class VirtualDeviceParams implements Parcelable {
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(mLockState);
dest.writeArraySet(mUsersWithMatchingAccounts);
+ dest.writeArraySet(mAllowedActivities);
+ dest.writeArraySet(mBlockedActivities);
}
@Override
@@ -118,8 +156,10 @@ public final class VirtualDeviceParams implements Parcelable {
return false;
}
VirtualDeviceParams that = (VirtualDeviceParams) o;
- return mLockState == that.mLockState && mUsersWithMatchingAccounts.equals(
- that.mUsersWithMatchingAccounts);
+ return mLockState == that.mLockState
+ && mUsersWithMatchingAccounts.equals(that.mUsersWithMatchingAccounts)
+ && Objects.equals(mAllowedActivities, that.mAllowedActivities)
+ && Objects.equals(mBlockedActivities, that.mBlockedActivities);
}
@Override
@@ -128,13 +168,17 @@ public final class VirtualDeviceParams implements Parcelable {
}
@Override
+ @NonNull
public String toString() {
return "VirtualDeviceParams("
+ " mLockState=" + mLockState
+ " mUsersWithMatchingAccounts=" + mUsersWithMatchingAccounts
+ + " mAllowedActivities=" + mAllowedActivities
+ + " mBlockedActivities=" + mBlockedActivities
+ ")";
}
+ @NonNull
public static final Parcelable.Creator<VirtualDeviceParams> CREATOR =
new Parcelable.Creator<VirtualDeviceParams>() {
public VirtualDeviceParams createFromParcel(Parcel in) {
@@ -153,6 +197,8 @@ public final class VirtualDeviceParams implements Parcelable {
private @LockState int mLockState = LOCK_STATE_ALWAYS_LOCKED;
private Set<UserHandle> mUsersWithMatchingAccounts;
+ @Nullable private Set<ComponentName> mBlockedActivities;
+ @Nullable private Set<ComponentName> mAllowedActivities;
/**
* Sets the lock state of the device. The permission {@code ADD_ALWAYS_UNLOCKED_DISPLAY}
@@ -171,12 +217,25 @@ public final class VirtualDeviceParams implements Parcelable {
/**
* Sets the user handles with matching managed accounts on the remote device to which
- * this virtual device is streaming.
+ * this virtual device is streaming. The caller is responsible for verifying the presence
+ * and legitimacy of a matching managed account on the remote device.
+ *
+ * <p>If the app streaming policy is
+ * {@link android.app.admin.DevicePolicyManager#NEARBY_STREAMING_SAME_MANAGED_ACCOUNT_ONLY
+ * NEARBY_STREAMING_SAME_MANAGED_ACCOUNT_ONLY}, activities not in
+ * {@code usersWithMatchingAccounts} will be blocked from starting.
+ *
+ * <p> If {@code usersWithMatchingAccounts} is empty (the default), streaming is allowed
+ * only if there is no device policy, or if the nearby streaming policy is
+ * {@link android.app.admin.DevicePolicyManager#NEARBY_STREAMING_ENABLED
+ * NEARBY_STREAMING_ENABLED}.
*
* @param usersWithMatchingAccounts A set of user handles with matching managed
* accounts on the remote device this is streaming to.
+ *
* @see android.app.admin.DevicePolicyManager#NEARBY_STREAMING_SAME_MANAGED_ACCOUNT_ONLY
*/
+ @NonNull
public Builder setUsersWithMatchingAccounts(
@NonNull Set<UserHandle> usersWithMatchingAccounts) {
mUsersWithMatchingAccounts = usersWithMatchingAccounts;
@@ -184,6 +243,54 @@ public final class VirtualDeviceParams implements Parcelable {
}
/**
+ * Sets the activities allowed to be launched in the virtual device. If
+ * {@code allowedActivities} is non-null, all activities other than the ones in the set will
+ * be blocked from launching.
+ *
+ * <p>{@code allowedActivities} and the set in {@link #setBlockedActivities(Set)} cannot
+ * both be non-null at the same time.
+ *
+ * @throws IllegalArgumentException if {@link #setBlockedActivities(Set)} has been set to a
+ * non-null value.
+ *
+ * @param allowedActivities A set of activity {@link ComponentName} allowed to be launched
+ * in the virtual device.
+ * @hide // TODO(b/194949534): Unhide this API
+ */
+ public Builder setAllowedActivities(@Nullable Set<ComponentName> allowedActivities) {
+ if (mBlockedActivities != null && allowedActivities != null) {
+ throw new IllegalArgumentException(
+ "Allowed activities and Blocked activities cannot both be set.");
+ }
+ mAllowedActivities = allowedActivities;
+ return this;
+ }
+
+ /**
+ * Sets the activities blocked from launching in the virtual device. If the {@code
+ * blockedActivities} is non-null, activities in the set are blocked from launching in the
+ * virtual device.
+ *
+ * {@code blockedActivities} and the set in {@link #setAllowedActivities(Set)} cannot both
+ * be non-null at the same time.
+ *
+ * @throws IllegalArgumentException if {@link #setAllowedActivities(Set)} has been set to a
+ * non-null value.
+ *
+ * @param blockedActivities A set of {@link ComponentName} to be blocked launching from
+ * virtual device.
+ * @hide // TODO(b/194949534): Unhide this API
+ */
+ public Builder setBlockedActivities(@Nullable Set<ComponentName> blockedActivities) {
+ if (mAllowedActivities != null && blockedActivities != null) {
+ throw new IllegalArgumentException(
+ "Allowed activities and Blocked activities cannot both be set.");
+ }
+ mBlockedActivities = blockedActivities;
+ return this;
+ }
+
+ /**
* Builds the {@link VirtualDeviceParams} instance.
*/
@NonNull
@@ -191,7 +298,13 @@ public final class VirtualDeviceParams implements Parcelable {
if (mUsersWithMatchingAccounts == null) {
mUsersWithMatchingAccounts = Collections.emptySet();
}
- return new VirtualDeviceParams(mLockState, mUsersWithMatchingAccounts);
+ if (mAllowedActivities != null && mBlockedActivities != null) {
+ // Should never reach here because the setters block this as well.
+ throw new IllegalStateException(
+ "Allowed activities and Blocked activities cannot both be set.");
+ }
+ return new VirtualDeviceParams(mLockState, mUsersWithMatchingAccounts,
+ mAllowedActivities, mBlockedActivities);
}
}
}
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index c714f507242e..b4f23028325b 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -48,10 +48,13 @@ import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.Trace;
import android.os.UserHandle;
+import android.os.UserManager;
import android.os.storage.StorageManager;
import android.permission.PermissionCheckerManager;
+import android.provider.MediaStore;
import android.text.TextUtils;
import android.util.Log;
+import android.util.SparseBooleanArray;
import com.android.internal.annotations.VisibleForTesting;
@@ -134,9 +137,18 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
private boolean mExported;
private boolean mNoPerms;
private boolean mSingleUser;
+ private SparseBooleanArray mUsersRedirectedToOwner = new SparseBooleanArray();
private ThreadLocal<AttributionSource> mCallingAttributionSource;
+ /**
+ * @hide
+ */
+ public static boolean isAuthorityRedirectedForCloneProfile(String authority) {
+ // For now, only MediaProvider gets redirected.
+ return MediaStore.AUTHORITY.equals(authority);
+ }
+
private Transport mTransport = new Transport();
/**
@@ -726,13 +738,47 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
}
boolean checkUser(int pid, int uid, Context context) {
- if (UserHandle.getUserId(uid) == context.getUserId() || mSingleUser) {
+ int callingUserId = UserHandle.getUserId(uid);
+
+ if (callingUserId == context.getUserId() || mSingleUser) {
return true;
}
- return context.checkPermission(INTERACT_ACROSS_USERS, pid, uid)
- == PackageManager.PERMISSION_GRANTED
+ if (context.checkPermission(INTERACT_ACROSS_USERS, pid, uid)
+ == PackageManager.PERMISSION_GRANTED
|| context.checkPermission(INTERACT_ACROSS_USERS_FULL, pid, uid)
- == PackageManager.PERMISSION_GRANTED;
+ == PackageManager.PERMISSION_GRANTED) {
+ return true;
+ }
+
+ if (isAuthorityRedirectedForCloneProfile(mAuthority)) {
+ if (mUsersRedirectedToOwner.indexOfKey(callingUserId) >= 0) {
+ return mUsersRedirectedToOwner.get(callingUserId);
+ }
+
+ // Haven't seen this user yet, look it up
+ try {
+ UserHandle callingUser = UserHandle.getUserHandleForUid(uid);
+ Context callingUserContext = mContext.createPackageContextAsUser("system",
+ 0, callingUser);
+ UserManager um = callingUserContext.getSystemService(UserManager.class);
+
+ if (um != null && um.isCloneProfile()) {
+ UserHandle parent = um.getProfileParent(callingUser);
+
+ if (parent != null && parent.equals(context.getUser())) {
+ mUsersRedirectedToOwner.put(callingUser.getIdentifier(), true);
+ return true;
+ }
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ // ignore
+ }
+
+ mUsersRedirectedToOwner.put(UserHandle.getUserId(uid), false);
+ return false;
+ }
+
+ return false;
}
/**
diff --git a/core/java/android/content/ContentProviderOperation.java b/core/java/android/content/ContentProviderOperation.java
index 30775b19ab00..0c065d9bd402 100644
--- a/core/java/android/content/ContentProviderOperation.java
+++ b/core/java/android/content/ContentProviderOperation.java
@@ -108,7 +108,7 @@ public class ContentProviderOperation implements Parcelable {
mExtras = null;
}
mSelection = source.readInt() != 0 ? source.readString8() : null;
- mSelectionArgs = source.readSparseArray(null);
+ mSelectionArgs = source.readSparseArray(null, java.lang.Object.class);
mExpectedCount = source.readInt() != 0 ? source.readInt() : null;
mYieldAllowed = source.readInt() != 0;
mExceptionAllowed = source.readInt() != 0;
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 2309fb636d44..ce2efcf4ac7f 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -41,6 +41,7 @@ import android.app.GameManager;
import android.app.IApplicationThread;
import android.app.IServiceConnection;
import android.app.VrManager;
+import android.app.ambientcontext.AmbientContextManager;
import android.app.people.PeopleManager;
import android.app.time.TimeManager;
import android.compat.annotation.UnsupportedAppUsage;
@@ -3829,7 +3830,7 @@ public abstract class Context {
PRINT_SERVICE,
CONSUMER_IR_SERVICE,
//@hide: TRUST_SERVICE,
- TV_IAPP_SERVICE,
+ TV_INTERACTIVE_APP_SERVICE,
TV_INPUT_SERVICE,
//@hide: TV_TUNER_RESOURCE_MGR_SERVICE,
//@hide: NETWORK_SCORE_SERVICE,
@@ -5356,13 +5357,13 @@ public abstract class Context {
/**
* Use with {@link #getSystemService(String)} to retrieve a
- * {@link android.media.tv.interactive.TvIAppManager} for interacting with TV interactive
- * applications (TV iApp) on the device.
+ * {@link android.media.tv.interactive.TvInteractiveAppManager} for interacting with TV
+ * interactive applications on the device.
*
* @see #getSystemService(String)
- * @see android.media.tv.interactive.TvIAppManager
+ * @see android.media.tv.interactive.TvInteractiveAppManager
*/
- public static final String TV_IAPP_SERVICE = "tv_iapp";
+ public static final String TV_INTERACTIVE_APP_SERVICE = "tv_interactive_app";
/**
* Use with {@link #getSystemService(String)} to retrieve a
@@ -5931,6 +5932,17 @@ public abstract class Context {
public static final String NEARBY_SERVICE = "nearby";
/**
+ * Use with {@link #getSystemService(String)} to retrieve a
+ * {@link android.app.ambientcontext.AmbientContextManager}.
+ *
+ * @see #getSystemService(String)
+ * @see AmbientContextManager
+ * @hide
+ */
+ @SystemApi
+ public static final String AMBIENT_CONTEXT_SERVICE = "ambient_context";
+
+ /**
* Determine whether the given permission is allowed for a particular
* process and user ID running in the system.
*
@@ -6417,10 +6429,10 @@ public abstract class Context {
* Triggers the asynchronous revocation of a permission.
*
* @param permName The name of the permission to be revoked.
- * @see #selfRevokePermissions(Collection)
+ * @see #revokeOwnPermissionsOnKill(Collection)
*/
- public void selfRevokePermission(@NonNull String permName) {
- selfRevokePermissions(Collections.singletonList(permName));
+ public void revokeOwnPermissionOnKill(@NonNull String permName) {
+ revokeOwnPermissionsOnKill(Collections.singletonList(permName));
}
/**
@@ -6445,7 +6457,7 @@ public abstract class Context {
* @see PackageManager#getGroupOfPlatformPermission(String, Executor, Consumer)
* @see PackageManager#getPlatformPermissionsForGroup(String, Executor, Consumer)
*/
- public void selfRevokePermissions(@NonNull Collection<String> permissions) {
+ public void revokeOwnPermissionsOnKill(@NonNull Collection<String> permissions) {
throw new AbstractMethodError("Must be overridden in implementing class");
}
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 805e499bba46..6ae768a44078 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -1016,8 +1016,8 @@ public class ContextWrapper extends Context {
}
@Override
- public void selfRevokePermissions(@NonNull Collection<String> permissions) {
- mBase.selfRevokePermissions(permissions);
+ public void revokeOwnPermissionsOnKill(@NonNull Collection<String> permissions) {
+ mBase.revokeOwnPermissionsOnKill(permissions);
}
@Override
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 58a7d8796ffb..7f00bcb1dccb 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -8190,6 +8190,37 @@ public class Intent implements Parcelable, Cloneable {
hasIntentInfo = true;
}
break;
+ case "--ed": {
+ String key = cmd.getNextArgRequired();
+ String value = cmd.getNextArgRequired();
+ intent.putExtra(key, Double.valueOf(value));
+ hasIntentInfo = true;
+ }
+ break;
+ case "--eda": {
+ String key = cmd.getNextArgRequired();
+ String value = cmd.getNextArgRequired();
+ String[] strings = value.split(",");
+ double[] list = new double[strings.length];
+ for (int i = 0; i < strings.length; i++) {
+ list[i] = Double.valueOf(strings[i]);
+ }
+ intent.putExtra(key, list);
+ hasIntentInfo = true;
+ }
+ break;
+ case "--edal": {
+ String key = cmd.getNextArgRequired();
+ String value = cmd.getNextArgRequired();
+ String[] strings = value.split(",");
+ ArrayList<Double> list = new ArrayList<>(strings.length);
+ for (int i = 0; i < strings.length; i++) {
+ list.add(Double.valueOf(strings[i]));
+ }
+ intent.putExtra(key, list);
+ hasIntentInfo = true;
+ }
+ break;
case "--esa": {
String key = cmd.getNextArgRequired();
String value = cmd.getNextArgRequired();
@@ -8435,25 +8466,30 @@ public class Intent implements Parcelable, Cloneable {
" [--ei <EXTRA_KEY> <EXTRA_INT_VALUE> ...]",
" [--el <EXTRA_KEY> <EXTRA_LONG_VALUE> ...]",
" [--ef <EXTRA_KEY> <EXTRA_FLOAT_VALUE> ...]",
+ " [--ed <EXTRA_KEY> <EXTRA_DOUBLE_VALUE> ...]",
" [--eu <EXTRA_KEY> <EXTRA_URI_VALUE> ...]",
" [--ecn <EXTRA_KEY> <EXTRA_COMPONENT_NAME_VALUE>]",
" [--eia <EXTRA_KEY> <EXTRA_INT_VALUE>[,<EXTRA_INT_VALUE...]]",
- " (mutiple extras passed as Integer[])",
+ " (multiple extras passed as Integer[])",
" [--eial <EXTRA_KEY> <EXTRA_INT_VALUE>[,<EXTRA_INT_VALUE...]]",
- " (mutiple extras passed as List<Integer>)",
+ " (multiple extras passed as List<Integer>)",
" [--ela <EXTRA_KEY> <EXTRA_LONG_VALUE>[,<EXTRA_LONG_VALUE...]]",
- " (mutiple extras passed as Long[])",
+ " (multiple extras passed as Long[])",
" [--elal <EXTRA_KEY> <EXTRA_LONG_VALUE>[,<EXTRA_LONG_VALUE...]]",
- " (mutiple extras passed as List<Long>)",
+ " (multiple extras passed as List<Long>)",
" [--efa <EXTRA_KEY> <EXTRA_FLOAT_VALUE>[,<EXTRA_FLOAT_VALUE...]]",
- " (mutiple extras passed as Float[])",
+ " (multiple extras passed as Float[])",
" [--efal <EXTRA_KEY> <EXTRA_FLOAT_VALUE>[,<EXTRA_FLOAT_VALUE...]]",
- " (mutiple extras passed as List<Float>)",
+ " (multiple extras passed as List<Float>)",
+ " [--eda <EXTRA_KEY> <EXTRA_DOUBLE_VALUE>[,<EXTRA_DOUBLE_VALUE...]]",
+ " (multiple extras passed as Double[])",
+ " [--edal <EXTRA_KEY> <EXTRA_DOUBLE_VALUE>[,<EXTRA_DOUBLE_VALUE...]]",
+ " (multiple extras passed as List<Double>)",
" [--esa <EXTRA_KEY> <EXTRA_STRING_VALUE>[,<EXTRA_STRING_VALUE...]]",
- " (mutiple extras passed as String[]; to embed a comma into a string,",
+ " (multiple extras passed as String[]; to embed a comma into a string,",
" escape it using \"\\,\")",
" [--esal <EXTRA_KEY> <EXTRA_STRING_VALUE>[,<EXTRA_STRING_VALUE...]]",
- " (mutiple extras passed as List<String>; to embed a comma into a string,",
+ " (multiple extras passed as List<String>; to embed a comma into a string,",
" escape it using \"\\,\")",
" [-f <FLAG>]",
" [--grant-read-uri-permission] [--grant-write-uri-permission]",
diff --git a/core/java/android/content/PeriodicSync.java b/core/java/android/content/PeriodicSync.java
index 432e81bad019..6830f5f34e75 100644
--- a/core/java/android/content/PeriodicSync.java
+++ b/core/java/android/content/PeriodicSync.java
@@ -84,7 +84,7 @@ public class PeriodicSync implements Parcelable {
}
private PeriodicSync(Parcel in) {
- this.account = in.readParcelable(null);
+ this.account = in.readParcelable(null, android.accounts.Account.class);
this.authority = in.readString();
this.extras = in.readBundle();
this.period = in.readLong();
diff --git a/core/java/android/content/SyncInfo.java b/core/java/android/content/SyncInfo.java
index 017a92b1e8bb..57101be6507e 100644
--- a/core/java/android/content/SyncInfo.java
+++ b/core/java/android/content/SyncInfo.java
@@ -99,7 +99,7 @@ public class SyncInfo implements Parcelable {
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
SyncInfo(Parcel parcel) {
authorityId = parcel.readInt();
- account = parcel.readParcelable(Account.class.getClassLoader());
+ account = parcel.readParcelable(Account.class.getClassLoader(), android.accounts.Account.class);
authority = parcel.readString();
startTime = parcel.readLong();
}
diff --git a/core/java/android/content/SyncRequest.java b/core/java/android/content/SyncRequest.java
index e1e6f75d152f..83ce84e7a5cb 100644
--- a/core/java/android/content/SyncRequest.java
+++ b/core/java/android/content/SyncRequest.java
@@ -174,7 +174,7 @@ public class SyncRequest implements Parcelable {
mIsAuthority = (in.readInt() != 0);
mIsExpedited = (in.readInt() != 0);
mIsScheduledAsExpeditedJob = (in.readInt() != 0);
- mAccountToSync = in.readParcelable(null);
+ mAccountToSync = in.readParcelable(null, android.accounts.Account.class);
mAuthority = in.readString();
}
diff --git a/core/java/android/content/UndoManager.java b/core/java/android/content/UndoManager.java
index 87afbf874b37..b2979f36e01b 100644
--- a/core/java/android/content/UndoManager.java
+++ b/core/java/android/content/UndoManager.java
@@ -777,7 +777,7 @@ public class UndoManager {
final int N = p.readInt();
for (int i=0; i<N; i++) {
UndoOwner owner = mManager.restoreOwner(p);
- UndoOperation op = (UndoOperation)p.readParcelable(loader);
+ UndoOperation op = (UndoOperation)p.readParcelable(loader, android.content.UndoOperation.class);
op.mOwner = owner;
mOperations.add(op);
}
diff --git a/core/java/android/content/UriPermission.java b/core/java/android/content/UriPermission.java
index d3a9cb812539..73477612985c 100644
--- a/core/java/android/content/UriPermission.java
+++ b/core/java/android/content/UriPermission.java
@@ -47,7 +47,7 @@ public final class UriPermission implements Parcelable {
/** {@hide} */
public UriPermission(Parcel in) {
- mUri = in.readParcelable(null);
+ mUri = in.readParcelable(null, android.net.Uri.class);
mModeFlags = in.readInt();
mPersistedTime = in.readLong();
}
diff --git a/core/java/android/content/om/OverlayManagerTransaction.java b/core/java/android/content/om/OverlayManagerTransaction.java
index 73be0ffbf467..868dab298108 100644
--- a/core/java/android/content/om/OverlayManagerTransaction.java
+++ b/core/java/android/content/om/OverlayManagerTransaction.java
@@ -67,7 +67,7 @@ public class OverlayManagerTransaction
mRequests = new ArrayList<>(size);
for (int i = 0; i < size; i++) {
final int request = source.readInt();
- final OverlayIdentifier overlay = source.readParcelable(null);
+ final OverlayIdentifier overlay = source.readParcelable(null, android.content.om.OverlayIdentifier.class);
final int userId = source.readInt();
final Bundle extras = source.readBundle(null);
mRequests.add(new Request(request, overlay, userId, extras));
diff --git a/core/java/android/content/pm/AppSearchShortcutInfo.java b/core/java/android/content/pm/AppSearchShortcutInfo.java
index 806091e2158d..8d9ef8530bfc 100644
--- a/core/java/android/content/pm/AppSearchShortcutInfo.java
+++ b/core/java/android/content/pm/AppSearchShortcutInfo.java
@@ -423,7 +423,7 @@ public class AppSearchShortcutInfo extends GenericDocument {
shortLabelResName, longLabel, longLabelResId, longLabelResName, disabledMessage,
disabledMessageResId, disabledMessageResName, categoriesSet, intents, rank, extras,
getCreationTimestampMillis(), flags, iconResId, iconResName, bitmapPath, iconUri,
- disabledReason, persons, locusId, null);
+ disabledReason, persons, locusId, null, null);
si.setImplicitRank(implicitRank);
if ((implicitRank & ShortcutInfo.RANK_CHANGED_BIT) != 0) {
si.setRankChanged();
diff --git a/core/java/android/content/pm/CrossProfileApps.java b/core/java/android/content/pm/CrossProfileApps.java
index 11b2ea1f6523..1e887582e5c3 100644
--- a/core/java/android/content/pm/CrossProfileApps.java
+++ b/core/java/android/content/pm/CrossProfileApps.java
@@ -15,6 +15,9 @@
*/
package android.content.pm;
+import static android.app.admin.DevicePolicyResources.Strings.Core.SWITCH_TO_PERSONAL_LABEL;
+import static android.app.admin.DevicePolicyResources.Strings.Core.SWITCH_TO_WORK_LABEL;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -22,6 +25,7 @@ import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.app.Activity;
import android.app.AppOpsManager.Mode;
+import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -241,12 +245,24 @@ public class CrossProfileApps {
public @NonNull CharSequence getProfileSwitchingLabel(@NonNull UserHandle userHandle) {
verifyCanAccessUser(userHandle);
- final int stringRes = mUserManager.isManagedProfile(userHandle.getIdentifier())
- ? R.string.managed_profile_label
- : R.string.user_owner_label;
+ final boolean isManagedProfile = mUserManager.isManagedProfile(userHandle.getIdentifier());
+ final DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
+ return dpm.getString(
+ getUpdatableProfileSwitchingLabelId(isManagedProfile),
+ () -> getDefaultProfileSwitchingLabel(isManagedProfile));
+ }
+
+ private String getUpdatableProfileSwitchingLabelId(boolean isManagedProfile) {
+ return isManagedProfile ? SWITCH_TO_WORK_LABEL : SWITCH_TO_PERSONAL_LABEL;
+ }
+
+ private String getDefaultProfileSwitchingLabel(boolean isManagedProfile) {
+ final int stringRes = isManagedProfile
+ ? R.string.managed_profile_label : R.string.user_owner_label;
return mResources.getString(stringRes);
}
+
/**
* Return a drawable that calling app can show to user for the semantic of profile switching --
* launching its own activity in specified user profile. For example, it may return a briefcase
diff --git a/core/java/android/content/pm/InstallSourceInfo.java b/core/java/android/content/pm/InstallSourceInfo.java
index a45bf7930509..84d2ca389611 100644
--- a/core/java/android/content/pm/InstallSourceInfo.java
+++ b/core/java/android/content/pm/InstallSourceInfo.java
@@ -61,7 +61,7 @@ public final class InstallSourceInfo implements Parcelable {
private InstallSourceInfo(Parcel source) {
mInitiatingPackageName = source.readString();
- mInitiatingPackageSigningInfo = source.readParcelable(SigningInfo.class.getClassLoader());
+ mInitiatingPackageSigningInfo = source.readParcelable(SigningInfo.class.getClassLoader(), android.content.pm.SigningInfo.class);
mOriginatingPackageName = source.readString();
mInstallingPackageName = source.readString();
}
diff --git a/core/java/android/content/pm/InstantAppInfo.java b/core/java/android/content/pm/InstantAppInfo.java
index 24d6a07ec4e8..d6cfb0e70693 100644
--- a/core/java/android/content/pm/InstantAppInfo.java
+++ b/core/java/android/content/pm/InstantAppInfo.java
@@ -65,7 +65,7 @@ public final class InstantAppInfo implements Parcelable {
mLabelText = parcel.readCharSequence();
mRequestedPermissions = parcel.readStringArray();
mGrantedPermissions = parcel.createStringArray();
- mApplicationInfo = parcel.readParcelable(null);
+ mApplicationInfo = parcel.readParcelable(null, android.content.pm.ApplicationInfo.class);
}
/**
diff --git a/core/java/android/content/pm/InstantAppIntentFilter.java b/core/java/android/content/pm/InstantAppIntentFilter.java
index 123d2ba5aa8d..721b2616fbfd 100644
--- a/core/java/android/content/pm/InstantAppIntentFilter.java
+++ b/core/java/android/content/pm/InstantAppIntentFilter.java
@@ -46,7 +46,7 @@ public final class InstantAppIntentFilter implements Parcelable {
InstantAppIntentFilter(Parcel in) {
mSplitName = in.readString();
- in.readList(mFilters, getClass().getClassLoader());
+ in.readList(mFilters, getClass().getClassLoader(), android.content.IntentFilter.class);
}
public String getSplitName() {
diff --git a/core/java/android/content/pm/InstantAppResolveInfo.java b/core/java/android/content/pm/InstantAppResolveInfo.java
index 98815647f0c3..6124638ccbcb 100644
--- a/core/java/android/content/pm/InstantAppResolveInfo.java
+++ b/core/java/android/content/pm/InstantAppResolveInfo.java
@@ -140,7 +140,7 @@ public final class InstantAppResolveInfo implements Parcelable {
mFilters = Collections.emptyList();
mVersionCode = -1;
} else {
- mDigest = in.readParcelable(null /*loader*/);
+ mDigest = in.readParcelable(null /*loader*/, android.content.pm.InstantAppResolveInfo.InstantAppDigest.class);
mPackageName = in.readString();
mFilters = new ArrayList<>();
in.readTypedList(mFilters, InstantAppIntentFilter.CREATOR);
diff --git a/core/java/android/content/pm/LauncherActivityInfoInternal.java b/core/java/android/content/pm/LauncherActivityInfoInternal.java
index 417f168940b6..46c415df7525 100644
--- a/core/java/android/content/pm/LauncherActivityInfoInternal.java
+++ b/core/java/android/content/pm/LauncherActivityInfoInternal.java
@@ -43,10 +43,10 @@ public class LauncherActivityInfoInternal implements Parcelable {
}
public LauncherActivityInfoInternal(Parcel source) {
- mActivityInfo = source.readParcelable(ActivityInfo.class.getClassLoader());
+ mActivityInfo = source.readParcelable(ActivityInfo.class.getClassLoader(), android.content.pm.ActivityInfo.class);
mComponentName = new ComponentName(mActivityInfo.packageName, mActivityInfo.name);
mIncrementalStatesInfo = source.readParcelable(
- IncrementalStatesInfo.class.getClassLoader());
+ IncrementalStatesInfo.class.getClassLoader(), android.content.pm.IncrementalStatesInfo.class);
}
public ComponentName getComponentName() {
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index d76dc782c367..08b07a73d4af 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -1754,11 +1754,11 @@ public class PackageInstaller {
installScenario = source.readInt();
sizeBytes = source.readLong();
appPackageName = source.readString();
- appIcon = source.readParcelable(null);
+ appIcon = source.readParcelable(null, android.graphics.Bitmap.class);
appLabel = source.readString();
- originatingUri = source.readParcelable(null);
+ originatingUri = source.readParcelable(null, android.net.Uri.class);
originatingUid = source.readInt();
- referrerUri = source.readParcelable(null);
+ referrerUri = source.readParcelable(null, android.net.Uri.class);
abiOverride = source.readString();
volumeUuid = source.readString();
grantedRuntimePermissions = source.readStringArray();
@@ -1770,7 +1770,7 @@ public class PackageInstaller {
forceQueryableOverride = source.readBoolean();
requiredInstalledVersionCode = source.readLong();
DataLoaderParamsParcel dataLoaderParamsParcel = source.readParcelable(
- DataLoaderParamsParcel.class.getClassLoader());
+ DataLoaderParamsParcel.class.getClassLoader(), android.content.pm.DataLoaderParamsParcel.class);
if (dataLoaderParamsParcel != null) {
dataLoaderParams = new DataLoaderParams(dataLoaderParamsParcel);
}
@@ -2563,13 +2563,13 @@ public class PackageInstaller {
installScenario = source.readInt();
sizeBytes = source.readLong();
appPackageName = source.readString();
- appIcon = source.readParcelable(null);
+ appIcon = source.readParcelable(null, android.graphics.Bitmap.class);
appLabel = source.readString();
installLocation = source.readInt();
- originatingUri = source.readParcelable(null);
+ originatingUri = source.readParcelable(null, android.net.Uri.class);
originatingUid = source.readInt();
- referrerUri = source.readParcelable(null);
+ referrerUri = source.readParcelable(null, android.net.Uri.class);
grantedRuntimePermissions = source.readStringArray();
whitelistedRestrictedPermissions = source.createStringArrayList();
autoRevokePermissionsMode = source.readInt();
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index c8f88f2edc62..1021d3e8e3fb 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -4077,6 +4077,14 @@ public abstract class PackageManager {
@SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_DREAM_OVERLAY = "android.software.dream_overlay";
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device
+ * supports window magnification.
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_WINDOW_MAGNIFICATION =
+ "android.software.window_magnification";
+
/** @hide */
public static final boolean APP_ENUMERATION_ENABLED_BY_DEFAULT = true;
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 701c5461362a..98cc8f6b0670 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -7300,7 +7300,7 @@ public class PackageParser {
splitFlags = dest.createIntArray();
splitPrivateFlags = dest.createIntArray();
baseHardwareAccelerated = (dest.readInt() == 1);
- applicationInfo = dest.readParcelable(boot);
+ applicationInfo = dest.readParcelable(boot, android.content.pm.ApplicationInfo.class);
if (applicationInfo.permission != null) {
applicationInfo.permission = applicationInfo.permission.intern();
}
@@ -7308,19 +7308,19 @@ public class PackageParser {
// We don't serialize the "owner" package and the application info object for each of
// these components, in order to save space and to avoid circular dependencies while
// serialization. We need to fix them all up here.
- dest.readParcelableList(permissions, boot);
+ dest.readParcelableList(permissions, boot, android.content.pm.PackageParser.Permission.class);
fixupOwner(permissions);
- dest.readParcelableList(permissionGroups, boot);
+ dest.readParcelableList(permissionGroups, boot, android.content.pm.PackageParser.PermissionGroup.class);
fixupOwner(permissionGroups);
- dest.readParcelableList(activities, boot);
+ dest.readParcelableList(activities, boot, android.content.pm.PackageParser.Activity.class);
fixupOwner(activities);
- dest.readParcelableList(receivers, boot);
+ dest.readParcelableList(receivers, boot, android.content.pm.PackageParser.Activity.class);
fixupOwner(receivers);
- dest.readParcelableList(providers, boot);
+ dest.readParcelableList(providers, boot, android.content.pm.PackageParser.Provider.class);
fixupOwner(providers);
- dest.readParcelableList(services, boot);
+ dest.readParcelableList(services, boot, android.content.pm.PackageParser.Service.class);
fixupOwner(services);
- dest.readParcelableList(instrumentation, boot);
+ dest.readParcelableList(instrumentation, boot, android.content.pm.PackageParser.Instrumentation.class);
fixupOwner(instrumentation);
dest.readStringList(requestedPermissions);
@@ -7330,10 +7330,10 @@ public class PackageParser {
protectedBroadcasts = dest.createStringArrayList();
internStringArrayList(protectedBroadcasts);
- parentPackage = dest.readParcelable(boot);
+ parentPackage = dest.readParcelable(boot, android.content.pm.PackageParser.Package.class);
childPackages = new ArrayList<>();
- dest.readParcelableList(childPackages, boot);
+ dest.readParcelableList(childPackages, boot, android.content.pm.PackageParser.Package.class);
if (childPackages.size() == 0) {
childPackages = null;
}
@@ -7367,7 +7367,7 @@ public class PackageParser {
}
preferredActivityFilters = new ArrayList<>();
- dest.readParcelableList(preferredActivityFilters, boot);
+ dest.readParcelableList(preferredActivityFilters, boot, android.content.pm.PackageParser.ActivityIntentInfo.class);
if (preferredActivityFilters.size() == 0) {
preferredActivityFilters = null;
}
@@ -7388,7 +7388,7 @@ public class PackageParser {
}
mSharedUserLabel = dest.readInt();
- mSigningDetails = dest.readParcelable(boot);
+ mSigningDetails = dest.readParcelable(boot, android.content.pm.PackageParser.SigningDetails.class);
mPreferredOrder = dest.readInt();
@@ -7400,19 +7400,19 @@ public class PackageParser {
configPreferences = new ArrayList<>();
- dest.readParcelableList(configPreferences, boot);
+ dest.readParcelableList(configPreferences, boot, android.content.pm.ConfigurationInfo.class);
if (configPreferences.size() == 0) {
configPreferences = null;
}
reqFeatures = new ArrayList<>();
- dest.readParcelableList(reqFeatures, boot);
+ dest.readParcelableList(reqFeatures, boot, android.content.pm.FeatureInfo.class);
if (reqFeatures.size() == 0) {
reqFeatures = null;
}
featureGroups = new ArrayList<>();
- dest.readParcelableList(featureGroups, boot);
+ dest.readParcelableList(featureGroups, boot, android.content.pm.FeatureGroupInfo.class);
if (featureGroups.size() == 0) {
featureGroups = null;
}
@@ -7809,13 +7809,13 @@ public class PackageParser {
private Permission(Parcel in) {
super(in);
final ClassLoader boot = Object.class.getClassLoader();
- info = in.readParcelable(boot);
+ info = in.readParcelable(boot, android.content.pm.PermissionInfo.class);
if (info.group != null) {
info.group = info.group.intern();
}
tree = (in.readInt() == 1);
- group = in.readParcelable(boot);
+ group = in.readParcelable(boot, android.content.pm.PackageParser.PermissionGroup.class);
}
public static final Parcelable.Creator CREATOR = new Parcelable.Creator<Permission>() {
@@ -7870,7 +7870,7 @@ public class PackageParser {
private PermissionGroup(Parcel in) {
super(in);
- info = in.readParcelable(Object.class.getClassLoader());
+ info = in.readParcelable(Object.class.getClassLoader(), android.content.pm.PermissionGroupInfo.class);
}
public static final Parcelable.Creator CREATOR = new Parcelable.Creator<PermissionGroup>() {
@@ -8163,7 +8163,7 @@ public class PackageParser {
private Activity(Parcel in) {
super(in);
- info = in.readParcelable(Object.class.getClassLoader());
+ info = in.readParcelable(Object.class.getClassLoader(), android.content.pm.ActivityInfo.class);
mHasMaxAspectRatio = in.readBoolean();
mHasMinAspectRatio = in.readBoolean();
@@ -8257,7 +8257,7 @@ public class PackageParser {
private Service(Parcel in) {
super(in);
- info = in.readParcelable(Object.class.getClassLoader());
+ info = in.readParcelable(Object.class.getClassLoader(), android.content.pm.ServiceInfo.class);
for (ServiceIntentInfo aii : intents) {
aii.service = this;
@@ -8347,7 +8347,7 @@ public class PackageParser {
private Provider(Parcel in) {
super(in);
- info = in.readParcelable(Object.class.getClassLoader());
+ info = in.readParcelable(Object.class.getClassLoader(), android.content.pm.ProviderInfo.class);
syncable = (in.readInt() == 1);
for (ProviderIntentInfo aii : intents) {
@@ -8439,7 +8439,7 @@ public class PackageParser {
private Instrumentation(Parcel in) {
super(in);
- info = in.readParcelable(Object.class.getClassLoader());
+ info = in.readParcelable(Object.class.getClassLoader(), android.content.pm.InstrumentationInfo.class);
if (info.targetPackage != null) {
info.targetPackage = info.targetPackage.intern();
diff --git a/core/java/android/content/pm/SharedLibraryInfo.java b/core/java/android/content/pm/SharedLibraryInfo.java
index f153566bf61a..43a4b17e5172 100644
--- a/core/java/android/content/pm/SharedLibraryInfo.java
+++ b/core/java/android/content/pm/SharedLibraryInfo.java
@@ -136,8 +136,8 @@ public final class SharedLibraryInfo implements Parcelable {
mName = parcel.readString8();
mVersion = parcel.readLong();
mType = parcel.readInt();
- mDeclaringPackage = parcel.readParcelable(null);
- mDependentPackages = parcel.readArrayList(null);
+ mDeclaringPackage = parcel.readParcelable(null, android.content.pm.VersionedPackage.class);
+ mDependentPackages = parcel.readArrayList(null, android.content.pm.VersionedPackage.class);
mDependencies = parcel.createTypedArrayList(SharedLibraryInfo.CREATOR);
mIsNative = parcel.readBoolean();
}
diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java
index 613fb84812f8..ab827aacbdc1 100644
--- a/core/java/android/content/pm/ShortcutInfo.java
+++ b/core/java/android/content/pm/ShortcutInfo.java
@@ -41,6 +41,7 @@ import android.os.Parcelable;
import android.os.PersistableBundle;
import android.os.UserHandle;
import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
import android.view.contentcapture.ContentCaptureContext;
@@ -50,9 +51,15 @@ import com.android.internal.util.Preconditions;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
/**
* Represents a shortcut that can be published via {@link ShortcutManager}.
@@ -463,6 +470,9 @@ public final class ShortcutInfo implements Parcelable {
private int mExcludedSurfaces;
+ @Nullable
+ private Map<String, Map<String, List<String>>> mCapabilityBindings;
+
private ShortcutInfo(Builder b) {
mUserId = b.mContext.getUserId();
@@ -490,7 +500,7 @@ public final class ShortcutInfo implements Parcelable {
mRank = b.mRank;
mExtras = b.mExtras;
mLocusId = b.mLocusId;
-
+ mCapabilityBindings = b.mCapabilityBindings;
mStartingThemeResName = b.mStartingThemeResId != 0
? b.mContext.getResources().getResourceName(b.mStartingThemeResId) : null;
updateTimestamp();
@@ -602,7 +612,7 @@ public final class ShortcutInfo implements Parcelable {
mLocusId = source.mLocusId;
mExcludedSurfaces = source.mExcludedSurfaces;
- // Just always keep it since it's cheep.
+ // Just always keep it since it's cheap.
mIconResId = source.mIconResId;
if ((cloneFlags & CLONE_REMOVE_NON_KEY_INFO) == 0) {
@@ -641,6 +651,7 @@ public final class ShortcutInfo implements Parcelable {
// Set this bit.
mFlags |= FLAG_KEY_FIELDS_ONLY;
}
+ mCapabilityBindings = source.mCapabilityBindings;
mStartingThemeResName = source.mStartingThemeResName;
}
@@ -968,6 +979,9 @@ public final class ShortcutInfo implements Parcelable {
if (source.mStartingThemeResName != null && !source.mStartingThemeResName.isEmpty()) {
mStartingThemeResName = source.mStartingThemeResName;
}
+ if (source.mCapabilityBindings != null) {
+ mCapabilityBindings = source.mCapabilityBindings;
+ }
}
/**
@@ -1039,6 +1053,9 @@ public final class ShortcutInfo implements Parcelable {
private int mStartingThemeResId;
+ @Nullable
+ private Map<String, Map<String, List<String>>> mCapabilityBindings;
+
private int mExcludedSurfaces;
/**
@@ -1401,6 +1418,53 @@ public final class ShortcutInfo implements Parcelable {
}
/**
+ * Associates a shortcut with a capability, and a parameter of that capability. Used when
+ * the shortcut is an instance of a capability.
+ *
+ * <P>This method can be called multiple times to add multiple parameters to the same
+ * capability.
+ *
+ * @param capability capability associated with the shortcut. e.g. actions.intent
+ * .START_EXERCISE.
+ * @param parameterName name of the parameter associated with given capability.
+ * e.g. exercise.name.
+ * @param parameterValues a list of values for that parameters. The first value will be
+ * the primary name, while the rest will be alternative names. If
+ * the values are empty, then the parameter will not be saved in
+ * the shortcut.
+ */
+ @NonNull
+ public Builder addCapabilityBinding(@NonNull String capability,
+ @Nullable String parameterName, @Nullable List<String> parameterValues) {
+ Objects.requireNonNull(capability);
+ if (capability.contains("/")) {
+ throw new IllegalArgumentException("Illegal character '/' is found in capability");
+ }
+ if (mCapabilityBindings == null) {
+ mCapabilityBindings = new ArrayMap<>(1);
+ }
+ if (!mCapabilityBindings.containsKey(capability)) {
+ mCapabilityBindings.put(capability, new ArrayMap<>(0));
+ }
+ if (parameterName == null || parameterValues == null || parameterValues.isEmpty()) {
+ return this;
+ }
+ if (parameterName.contains("/")) {
+ throw new IllegalArgumentException(
+ "Illegal character '/' is found in parameter name");
+ }
+ final Map<String, List<String>> params = mCapabilityBindings.get(capability);
+ if (!params.containsKey(parameterName)) {
+ params.put(parameterName, parameterValues);
+ return this;
+ }
+ params.put(parameterName,
+ Stream.of(params.get(parameterName), parameterValues)
+ .flatMap(Collection::stream).collect(Collectors.toList()));
+ return this;
+ }
+
+ /**
* Sets which surfaces a shortcut will be excluded from.
*
* If the shortcut is set to be excluded from {@link #SURFACE_LAUNCHER}, shortcuts will be
@@ -2176,13 +2240,51 @@ public final class ShortcutInfo implements Parcelable {
return (mExcludedSurfaces & surface) == 0;
}
+ /**
+ * @hide
+ */
+ public Map<String, Map<String, List<String>>> getCapabilityBindings() {
+ return mCapabilityBindings;
+ }
+
+ /**
+ * Return true if the shortcut is or can be used in specified capability.
+ */
+ public boolean hasCapability(@NonNull String capability) {
+ Objects.requireNonNull(capability);
+ return mCapabilityBindings != null && mCapabilityBindings.containsKey(capability);
+ }
+
+ /**
+ * Returns the values of specified parameter in associated with given capability.
+ *
+ * @param capability capability associated with the shortcut. e.g. actions.intent
+ * .START_EXERCISE.
+ * @param parameterName name of the parameter associated with given capability.
+ * e.g. exercise.name.
+ */
+ @NonNull
+ public List<String> getCapabilityParameterValues(
+ @NonNull String capability, @NonNull String parameterName) {
+ Objects.requireNonNull(capability);
+ Objects.requireNonNull(parameterName);
+ if (mCapabilityBindings == null) {
+ return Collections.emptyList();
+ }
+ final Map<String, List<String>> param = mCapabilityBindings.get(capability);
+ if (param == null || !param.containsKey(parameterName)) {
+ return Collections.emptyList();
+ }
+ return param.get(parameterName);
+ }
+
private ShortcutInfo(Parcel source) {
final ClassLoader cl = getClass().getClassLoader();
mUserId = source.readInt();
mId = source.readString8();
mPackageName = source.readString8();
- mActivity = source.readParcelable(cl);
+ mActivity = source.readParcelable(cl, android.content.ComponentName.class);
mFlags = source.readInt();
mIconResId = source.readInt();
mLastChangedTimestamp = source.readLong();
@@ -2192,7 +2294,7 @@ public final class ShortcutInfo implements Parcelable {
return; // key information only.
}
- mIcon = source.readParcelable(cl);
+ mIcon = source.readParcelable(cl, android.graphics.drawable.Icon.class);
mTitle = source.readCharSequence();
mTitleResId = source.readInt();
mText = source.readCharSequence();
@@ -2202,7 +2304,7 @@ public final class ShortcutInfo implements Parcelable {
mIntents = source.readParcelableArray(cl, Intent.class);
mIntentPersistableExtrases = source.readParcelableArray(cl, PersistableBundle.class);
mRank = source.readInt();
- mExtras = source.readParcelable(cl);
+ mExtras = source.readParcelable(cl, android.os.PersistableBundle.class);
mBitmapPath = source.readString8();
mIconResName = source.readString8();
@@ -2221,10 +2323,19 @@ public final class ShortcutInfo implements Parcelable {
}
mPersons = source.readParcelableArray(cl, Person.class);
- mLocusId = source.readParcelable(cl);
+ mLocusId = source.readParcelable(cl, android.content.LocusId.class);
mIconUri = source.readString8();
mStartingThemeResName = source.readString8();
mExcludedSurfaces = source.readInt();
+
+ final Map<String, Map> rawCapabilityBindings = source.readHashMap(
+ /*loader*/ null, /*clazzKey*/ String.class, /*clazzValue*/ HashMap.class);
+ if (rawCapabilityBindings != null && !rawCapabilityBindings.isEmpty()) {
+ final Map<String, Map<String, List<String>>> capabilityBindings =
+ new ArrayMap<>(rawCapabilityBindings.size());
+ rawCapabilityBindings.forEach(capabilityBindings::put);
+ mCapabilityBindings = capabilityBindings;
+ }
}
@Override
@@ -2278,6 +2389,7 @@ public final class ShortcutInfo implements Parcelable {
dest.writeString8(mIconUri);
dest.writeString8(mStartingThemeResName);
dest.writeInt(mExcludedSurfaces);
+ dest.writeMap(mCapabilityBindings);
}
public static final @NonNull Creator<ShortcutInfo> CREATOR =
@@ -2529,7 +2641,8 @@ public final class ShortcutInfo implements Parcelable {
long lastChangedTimestamp,
int flags, int iconResId, String iconResName, String bitmapPath, String iconUri,
int disabledReason, Person[] persons, LocusId locusId,
- @Nullable String startingThemeResName) {
+ @Nullable String startingThemeResName,
+ @Nullable Map<String, Map<String, List<String>>> capabilityBindings) {
mUserId = userId;
mId = id;
mPackageName = packageName;
@@ -2559,5 +2672,6 @@ public final class ShortcutInfo implements Parcelable {
mPersons = persons;
mLocusId = locusId;
mStartingThemeResName = startingThemeResName;
+ mCapabilityBindings = capabilityBindings;
}
}
diff --git a/core/java/android/content/pm/ShortcutManager.java b/core/java/android/content/pm/ShortcutManager.java
index be0d934f5133..7dbfd08310be 100644
--- a/core/java/android/content/pm/ShortcutManager.java
+++ b/core/java/android/content/pm/ShortcutManager.java
@@ -704,8 +704,8 @@ public class ShortcutManager {
}
private ShareShortcutInfo(@NonNull Parcel in) {
- mShortcutInfo = in.readParcelable(ShortcutInfo.class.getClassLoader());
- mTargetComponent = in.readParcelable(ComponentName.class.getClassLoader());
+ mShortcutInfo = in.readParcelable(ShortcutInfo.class.getClassLoader(), android.content.pm.ShortcutInfo.class);
+ mTargetComponent = in.readParcelable(ComponentName.class.getClassLoader(), android.content.ComponentName.class);
}
@NonNull
diff --git a/core/java/android/content/pm/ShortcutQueryWrapper.java b/core/java/android/content/pm/ShortcutQueryWrapper.java
index c6134416adbc..64337d86f7ec 100644
--- a/core/java/android/content/pm/ShortcutQueryWrapper.java
+++ b/core/java/android/content/pm/ShortcutQueryWrapper.java
@@ -143,7 +143,7 @@ public final class ShortcutQueryWrapper extends LauncherApps.ShortcutQuery imple
List<LocusId> locusIds = null;
if ((flg & 0x8) != 0) {
locusIds = new ArrayList<>();
- in.readParcelableList(locusIds, LocusId.class.getClassLoader());
+ in.readParcelableList(locusIds, LocusId.class.getClassLoader(), android.content.LocusId.class);
}
ComponentName activity = (flg & 0x10) == 0 ? null
: (ComponentName) in.readTypedObject(ComponentName.CREATOR);
diff --git a/core/java/android/content/pm/VerifierInfo.java b/core/java/android/content/pm/VerifierInfo.java
index 3e69ff555946..868bb9cb995c 100644
--- a/core/java/android/content/pm/VerifierInfo.java
+++ b/core/java/android/content/pm/VerifierInfo.java
@@ -59,7 +59,7 @@ public class VerifierInfo implements Parcelable {
private VerifierInfo(Parcel source) {
packageName = source.readString();
- publicKey = (PublicKey) source.readSerializable();
+ publicKey = (PublicKey) source.readSerializable(java.security.PublicKey.class.getClassLoader(), java.security.PublicKey.class);
}
@Override
diff --git a/core/java/android/graphics/fonts/FontUpdateRequest.java b/core/java/android/graphics/fonts/FontUpdateRequest.java
index cda1638a24dc..dae09f0977a4 100644
--- a/core/java/android/graphics/fonts/FontUpdateRequest.java
+++ b/core/java/android/graphics/fonts/FontUpdateRequest.java
@@ -235,7 +235,7 @@ public final class FontUpdateRequest implements Parcelable {
public Family createFromParcel(Parcel source) {
String familyName = source.readString8();
List<Font> fonts = source.readParcelableList(
- new ArrayList<>(), Font.class.getClassLoader());
+ new ArrayList<>(), Font.class.getClassLoader(), android.graphics.fonts.FontUpdateRequest.Font.class);
return new Family(familyName, fonts);
}
@@ -379,9 +379,9 @@ public final class FontUpdateRequest implements Parcelable {
protected FontUpdateRequest(Parcel in) {
mType = in.readInt();
- mFd = in.readParcelable(ParcelFileDescriptor.class.getClassLoader());
+ mFd = in.readParcelable(ParcelFileDescriptor.class.getClassLoader(), android.os.ParcelFileDescriptor.class);
mSignature = in.readBlob();
- mFontFamily = in.readParcelable(FontConfig.FontFamily.class.getClassLoader());
+ mFontFamily = in.readParcelable(FontConfig.FontFamily.class.getClassLoader(), android.graphics.fonts.FontUpdateRequest.Family.class);
}
public @Type int getType() {
diff --git a/core/java/android/hardware/CameraStreamStats.java b/core/java/android/hardware/CameraStreamStats.java
index 41d1e2523a9b..ed22de8dd594 100644
--- a/core/java/android/hardware/CameraStreamStats.java
+++ b/core/java/android/hardware/CameraStreamStats.java
@@ -15,6 +15,7 @@
*/
package android.hardware;
+import android.hardware.camera2.params.DynamicRangeProfiles;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
@@ -45,6 +46,7 @@ public class CameraStreamStats implements Parcelable {
private int mHistogramType;
private float[] mHistogramBins;
private long[] mHistogramCounts;
+ private int mDynamicRangeProfile;
private static final String TAG = "CameraStreamStats";
@@ -60,11 +62,12 @@ public class CameraStreamStats implements Parcelable {
mMaxHalBuffers = 0;
mMaxAppBuffers = 0;
mHistogramType = HISTOGRAM_TYPE_UNKNOWN;
+ mDynamicRangeProfile = DynamicRangeProfiles.STANDARD;
}
public CameraStreamStats(int width, int height, int format,
int dataSpace, long usage, long requestCount, long errorCount,
- int startLatencyMs, int maxHalBuffers, int maxAppBuffers) {
+ int startLatencyMs, int maxHalBuffers, int maxAppBuffers, int dynamicRangeProfile) {
mWidth = width;
mHeight = height;
mFormat = format;
@@ -76,6 +79,7 @@ public class CameraStreamStats implements Parcelable {
mMaxHalBuffers = maxHalBuffers;
mMaxAppBuffers = maxAppBuffers;
mHistogramType = HISTOGRAM_TYPE_UNKNOWN;
+ mDynamicRangeProfile = dynamicRangeProfile;
}
public static final @android.annotation.NonNull Parcelable.Creator<CameraStreamStats> CREATOR =
@@ -121,6 +125,7 @@ public class CameraStreamStats implements Parcelable {
dest.writeInt(mHistogramType);
dest.writeFloatArray(mHistogramBins);
dest.writeLongArray(mHistogramCounts);
+ dest.writeInt(mDynamicRangeProfile);
}
public void readFromParcel(Parcel in) {
@@ -137,6 +142,7 @@ public class CameraStreamStats implements Parcelable {
mHistogramType = in.readInt();
mHistogramBins = in.createFloatArray();
mHistogramCounts = in.createLongArray();
+ mDynamicRangeProfile = in.readInt();
}
public int getWidth() {
@@ -190,4 +196,8 @@ public class CameraStreamStats implements Parcelable {
public long[] getHistogramCounts() {
return mHistogramCounts;
}
+
+ public int getDynamicRangeProfile() {
+ return mDynamicRangeProfile;
+ }
}
diff --git a/core/java/android/hardware/HardwareBuffer.java b/core/java/android/hardware/HardwareBuffer.java
index 4683d252b68a..acceb654a959 100644
--- a/core/java/android/hardware/HardwareBuffer.java
+++ b/core/java/android/hardware/HardwareBuffer.java
@@ -110,9 +110,9 @@ public final class HardwareBuffer implements Parcelable, AutoCloseable {
@Retention(RetentionPolicy.SOURCE)
@LongDef(flag = true, value = {USAGE_CPU_READ_RARELY, USAGE_CPU_READ_OFTEN,
USAGE_CPU_WRITE_RARELY, USAGE_CPU_WRITE_OFTEN, USAGE_GPU_SAMPLED_IMAGE,
- USAGE_GPU_COLOR_OUTPUT, USAGE_PROTECTED_CONTENT, USAGE_VIDEO_ENCODE,
- USAGE_GPU_DATA_BUFFER, USAGE_SENSOR_DIRECT_DATA, USAGE_GPU_CUBE_MAP,
- USAGE_GPU_MIPMAP_COMPLETE})
+ USAGE_GPU_COLOR_OUTPUT, USAGE_COMPOSER_OVERLAY, USAGE_PROTECTED_CONTENT,
+ USAGE_VIDEO_ENCODE, USAGE_GPU_DATA_BUFFER, USAGE_SENSOR_DIRECT_DATA,
+ USAGE_GPU_CUBE_MAP, USAGE_GPU_MIPMAP_COMPLETE, USAGE_FRONT_BUFFER})
public @interface Usage {};
@Usage
@@ -151,6 +151,12 @@ public final class HardwareBuffer implements Parcelable, AutoCloseable {
public static final long USAGE_GPU_CUBE_MAP = 1 << 25;
/** Usage: The buffer contains a complete mipmap hierarchy */
public static final long USAGE_GPU_MIPMAP_COMPLETE = 1 << 26;
+ /** Usage: The buffer is used for front-buffer rendering. When front-buffering rendering is
+ * specified, different usages may adjust their behavior as a result. For example, when
+ * used as USAGE_GPU_COLOR_OUTPUT the buffer will behave similar to a single-buffered window.
+ * When used with USAGE_COMPOSER_OVERLAY, the system will try to prioritize the buffer
+ * receiving an overlay plane & avoid caching it in intermediate composition buffers. */
+ public static final long USAGE_FRONT_BUFFER = 1 << 32;
/**
* Creates a new <code>HardwareBuffer</code> instance.
diff --git a/core/java/android/hardware/biometrics/PromptInfo.java b/core/java/android/hardware/biometrics/PromptInfo.java
index e6b762a64384..0c03948e5368 100644
--- a/core/java/android/hardware/biometrics/PromptInfo.java
+++ b/core/java/android/hardware/biometrics/PromptInfo.java
@@ -65,7 +65,7 @@ public class PromptInfo implements Parcelable {
mAuthenticators = in.readInt();
mDisallowBiometricsIfPolicyExists = in.readBoolean();
mReceiveSystemEvents = in.readBoolean();
- mAllowedSensorIds = in.readArrayList(Integer.class.getClassLoader());
+ mAllowedSensorIds = in.readArrayList(Integer.class.getClassLoader(), java.lang.Integer.class);
mAllowBackgroundAuthentication = in.readBoolean();
mIgnoreEnrollmentState = in.readBoolean();
}
diff --git a/core/java/android/hardware/biometrics/SensorPropertiesInternal.java b/core/java/android/hardware/biometrics/SensorPropertiesInternal.java
index f365ee6066d0..1490ea1592a5 100644
--- a/core/java/android/hardware/biometrics/SensorPropertiesInternal.java
+++ b/core/java/android/hardware/biometrics/SensorPropertiesInternal.java
@@ -60,7 +60,7 @@ public class SensorPropertiesInternal implements Parcelable {
sensorStrength = in.readInt();
maxEnrollmentsPerUser = in.readInt();
componentInfo = new ArrayList<>();
- in.readList(componentInfo, ComponentInfoInternal.class.getClassLoader());
+ in.readList(componentInfo, ComponentInfoInternal.class.getClassLoader(), android.hardware.biometrics.ComponentInfoInternal.class);
resetLockoutRequiresHardwareAuthToken = in.readBoolean();
resetLockoutRequiresChallenge = in.readBoolean();
}
diff --git a/core/java/android/hardware/camera2/CameraCaptureSession.java b/core/java/android/hardware/camera2/CameraCaptureSession.java
index bd4746369811..691690c09e0e 100644
--- a/core/java/android/hardware/camera2/CameraCaptureSession.java
+++ b/core/java/android/hardware/camera2/CameraCaptureSession.java
@@ -321,6 +321,9 @@ public abstract class CameraCaptureSession implements AutoCloseable {
* can submit reprocess capture requests. Submitting a reprocess request to a regular capture
* session will result in an {@link IllegalArgumentException}.</p>
*
+ * <p>Submitting a request that targets Surfaces with an unsupported dynamic range combination
+ * will result in an {@link IllegalArgumentException}.</p>
+ *
* @param request the settings for this capture
* @param listener The callback object to notify once this request has been
* processed. If null, no metadata will be produced for this capture,
@@ -347,13 +350,15 @@ public abstract class CameraCaptureSession implements AutoCloseable {
* a different session; or the capture targets a Surface in
* the middle of being {@link #prepare prepared}; or the
* handler is null, the listener is not null, and the calling
- * thread has no looper.
+ * thread has no looper; or the request targets Surfaces with
+ * an unsupported dynamic range combination
*
* @see #captureBurst
* @see #setRepeatingRequest
* @see #setRepeatingBurst
* @see #abortCaptures
* @see CameraDevice#createReprocessableCaptureSession
+ * @see android.hardware.camera2.params.DynamicRangeProfiles#getProfileCaptureRequestConstraints
*/
public abstract int capture(@NonNull CaptureRequest request,
@Nullable CaptureCallback listener, @Nullable Handler handler)
@@ -389,13 +394,16 @@ public abstract class CameraCaptureSession implements AutoCloseable {
* request was created with a {@link TotalCaptureResult} from
* a different session; or the capture targets a Surface in
* the middle of being {@link #prepare prepared}; or the
- * executor is null, or the listener is not null.
+ * executor is null, or the listener is not null;
+ * or the request targets Surfaces with an unsupported dynamic
+ * range combination;
*
* @see #captureBurst
* @see #setRepeatingRequest
* @see #setRepeatingBurst
* @see #abortCaptures
* @see CameraDevice#createReprocessableCaptureSession
+ * @see android.hardware.camera2.params.DynamicRangeProfiles#getProfileCaptureRequestConstraints
*/
public int captureSingleRequest(@NonNull CaptureRequest request,
@NonNull @CallbackExecutor Executor executor, @NonNull CaptureCallback listener)
@@ -427,6 +435,9 @@ public abstract class CameraCaptureSession implements AutoCloseable {
* can submit reprocess capture requests. Submitting a reprocess request to a regular
* capture session will result in an {@link IllegalArgumentException}.</p>
*
+ * <p>Submitting a request that targets Surfaces with an unsupported dynamic range combination
+ * will result in an {@link IllegalArgumentException}.</p>
+ *
* @param requests the list of settings for this burst capture
* @param listener The callback object to notify each time one of the
* requests in the burst has been processed. If null, no metadata will be
@@ -454,12 +465,15 @@ public abstract class CameraCaptureSession implements AutoCloseable {
* {@link TotalCaptureResult} from a different session; or one
* of the captures targets a Surface in the middle of being
* {@link #prepare prepared}; or if the handler is null, the
- * listener is not null, and the calling thread has no looper.
+ * listener is not null, and the calling thread has no looper;
+ * or the request targets Surfaces with an unsupported dynamic
+ * range combination.
*
* @see #capture
* @see #setRepeatingRequest
* @see #setRepeatingBurst
* @see #abortCaptures
+ * @see android.hardware.camera2.params.DynamicRangeProfiles#getProfileCaptureRequestConstraints
*/
public abstract int captureBurst(@NonNull List<CaptureRequest> requests,
@Nullable CaptureCallback listener, @Nullable Handler handler)
@@ -499,12 +513,15 @@ public abstract class CameraCaptureSession implements AutoCloseable {
* {@link TotalCaptureResult} from a different session; or one
* of the captures targets a Surface in the middle of being
* {@link #prepare prepared}; or if the executor is null; or if
- * the listener is null.
+ * the listener is null;
+ * or the request targets Surfaces with an unsupported dynamic
+ * range combination.
*
* @see #capture
* @see #setRepeatingRequest
* @see #setRepeatingBurst
* @see #abortCaptures
+ * @see android.hardware.camera2.params.DynamicRangeProfiles#getProfileCaptureRequestConstraints
*/
public int captureBurstRequests(@NonNull List<CaptureRequest> requests,
@NonNull @CallbackExecutor Executor executor, @NonNull CaptureCallback listener)
@@ -545,6 +562,9 @@ public abstract class CameraCaptureSession implements AutoCloseable {
* single reprocess input image. The request must be capturing images from the camera. If a
* reprocess capture request is submitted, this method will throw IllegalArgumentException.</p>
*
+ * <p>Submitting a request that targets Surfaces with an unsupported dynamic range combination
+ * will result in an {@link IllegalArgumentException}.</p>
+ *
* @param request the request to repeat indefinitely
* @param listener The callback object to notify every time the
* request finishes processing. If null, no metadata will be
@@ -567,13 +587,16 @@ public abstract class CameraCaptureSession implements AutoCloseable {
* is a reprocess capture request; or the capture targets a
* Surface in the middle of being {@link #prepare prepared}; or
* the handler is null, the listener is not null, and the
- * calling thread has no looper; or no requests were passed in.
+ * calling thread has no looper; or no requests were passed in;
+ * or the request targets Surfaces with an unsupported dynamic
+ * range combination.
*
* @see #capture
* @see #captureBurst
* @see #setRepeatingBurst
* @see #stopRepeating
* @see #abortCaptures
+ * @see android.hardware.camera2.params.DynamicRangeProfiles#getProfileCaptureRequestConstraints
*/
public abstract int setRepeatingRequest(@NonNull CaptureRequest request,
@Nullable CaptureCallback listener, @Nullable Handler handler)
@@ -604,13 +627,16 @@ public abstract class CameraCaptureSession implements AutoCloseable {
* that are not currently configured as outputs; or the request
* is a reprocess capture request; or the capture targets a
* Surface in the middle of being {@link #prepare prepared}; or
- * the executor is null; or the listener is null.
+ * the executor is null; or the listener is null;
+ * or the request targets Surfaces with an unsupported dynamic
+ * range combination.
*
* @see #capture
* @see #captureBurst
* @see #setRepeatingBurst
* @see #stopRepeating
* @see #abortCaptures
+ * @see android.hardware.camera2.params.DynamicRangeProfiles#getProfileCaptureRequestConstraints
*/
public int setSingleRepeatingRequest(@NonNull CaptureRequest request,
@NonNull @CallbackExecutor Executor executor, @NonNull CaptureCallback listener)
@@ -655,6 +681,9 @@ public abstract class CameraCaptureSession implements AutoCloseable {
* single reprocess input image. The request must be capturing images from the camera. If a
* reprocess capture request is submitted, this method will throw IllegalArgumentException.</p>
*
+ * <p>Submitting a request that targets Surfaces with an unsupported dynamic range combination
+ * will result in an {@link IllegalArgumentException}.</p>
+ *
* @param requests the list of requests to cycle through indefinitely
* @param listener The callback object to notify each time one of the
* requests in the repeating bursts has finished processing. If null, no
@@ -678,13 +707,16 @@ public abstract class CameraCaptureSession implements AutoCloseable {
* targets a Surface in the middle of being
* {@link #prepare prepared}; or the handler is null, the
* listener is not null, and the calling thread has no looper;
- * or no requests were passed in.
+ * or no requests were passed in;
+ * or the request targets Surfaces with an unsupported dynamic
+ * range combination.
*
* @see #capture
* @see #captureBurst
* @see #setRepeatingRequest
* @see #stopRepeating
* @see #abortCaptures
+ * @see android.hardware.camera2.params.DynamicRangeProfiles#getProfileCaptureRequestConstraints
*/
public abstract int setRepeatingBurst(@NonNull List<CaptureRequest> requests,
@Nullable CaptureCallback listener, @Nullable Handler handler)
@@ -717,13 +749,16 @@ public abstract class CameraCaptureSession implements AutoCloseable {
* is a reprocess capture request; or one of the captures
* targets a Surface in the middle of being
* {@link #prepare prepared}; or the executor is null; or the
- * listener is null.
+ * listener is null;
+ * or the request targets Surfaces with an unsupported dynamic
+ * range combination.
*
* @see #capture
* @see #captureBurst
* @see #setRepeatingRequest
* @see #stopRepeating
* @see #abortCaptures
+ * @see android.hardware.camera2.params.DynamicRangeProfiles#getProfileCaptureRequestConstraints
*/
public int setRepeatingBurstRequests(@NonNull List<CaptureRequest> requests,
@NonNull @CallbackExecutor Executor executor, @NonNull CaptureCallback listener)
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 48a9121ef7f2..d2dc314585d6 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -461,7 +461,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
public @Nullable RecommendedStreamConfigurationMap getRecommendedStreamConfigurationMap(
@RecommendedStreamConfigurationMap.RecommendedUsecase int usecase) {
if (((usecase >= RecommendedStreamConfigurationMap.USECASE_PREVIEW) &&
- (usecase <= RecommendedStreamConfigurationMap.USECASE_LOW_LATENCY_SNAPSHOT)) ||
+ (usecase <= RecommendedStreamConfigurationMap.USECASE_10BIT_OUTPUT)) ||
((usecase >= RecommendedStreamConfigurationMap.USECASE_VENDOR_START) &&
(usecase < RecommendedStreamConfigurationMap.MAX_USECASE_COUNT))) {
if (mRecommendedConfigurations == null) {
@@ -2213,6 +2213,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_OFFLINE_PROCESSING OFFLINE_PROCESSING}</li>
* <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR ULTRA_HIGH_RESOLUTION_SENSOR}</li>
* <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_REMOSAIC_REPROCESSING REMOSAIC_REPROCESSING}</li>
+ * <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT DYNAMIC_RANGE_TEN_BIT}</li>
* </ul>
*
* <p>This key is available on all devices.</p>
@@ -2236,6 +2237,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* @see #REQUEST_AVAILABLE_CAPABILITIES_OFFLINE_PROCESSING
* @see #REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR
* @see #REQUEST_AVAILABLE_CAPABILITIES_REMOSAIC_REPROCESSING
+ * @see #REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT
*/
@PublicKey
@NonNull
@@ -2379,6 +2381,86 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
new Key<int[]>("android.request.characteristicKeysNeedingPermission", int[].class);
/**
+ * <p>Devices supporting the 10-bit output capability
+ * {@link android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT }
+ * must list their supported dynamic range profiles along with capture request
+ * constraints for specific profile combinations.</p>
+ * <p>Camera clients can retrieve the list of supported 10-bit dynamic range profiles by calling
+ * {@link android.hardware.camera2.params.DynamicRangeProfiles#getSupportedProfiles }.
+ * Any of them can be configured by setting OutputConfiguration dynamic range profile in
+ * {@link android.hardware.camera2.params.OutputConfiguration#setDynamicRangeProfile }.
+ * Clients can also check if there are any constraints that limit the combination
+ * of supported profiles that can be referenced within a single capture request by calling
+ * {@link android.hardware.camera2.params.DynamicRangeProfiles#getProfileCaptureRequestConstraints }.</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ */
+ @PublicKey
+ @NonNull
+ @SyntheticKey
+ public static final Key<android.hardware.camera2.params.DynamicRangeProfiles> REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES =
+ new Key<android.hardware.camera2.params.DynamicRangeProfiles>("android.request.availableDynamicRangeProfiles", android.hardware.camera2.params.DynamicRangeProfiles.class);
+
+ /**
+ * <p>A map of all available 10-bit dynamic range profiles along with their
+ * capture request constraints.</p>
+ * <p>Devices supporting the 10-bit output capability
+ * {@link android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT }
+ * must list their supported dynamic range profiles. In case the camera is not able to
+ * support every possible profile combination within a single capture request, then the
+ * constraints must be listed here as well.</p>
+ * <p><b>Possible values:</b></p>
+ * <ul>
+ * <li>{@link #REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_STANDARD STANDARD}</li>
+ * <li>{@link #REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_HLG10 HLG10}</li>
+ * <li>{@link #REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_HDR10 HDR10}</li>
+ * <li>{@link #REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_HDR10_PLUS HDR10_PLUS}</li>
+ * <li>{@link #REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_10B_HDR_REF DOLBY_VISION_10B_HDR_REF}</li>
+ * <li>{@link #REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_10B_HDR_REF_PO DOLBY_VISION_10B_HDR_REF_PO}</li>
+ * <li>{@link #REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_10B_HDR_OEM DOLBY_VISION_10B_HDR_OEM}</li>
+ * <li>{@link #REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_10B_HDR_OEM_PO DOLBY_VISION_10B_HDR_OEM_PO}</li>
+ * <li>{@link #REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_8B_HDR_REF DOLBY_VISION_8B_HDR_REF}</li>
+ * <li>{@link #REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_8B_HDR_REF_PO DOLBY_VISION_8B_HDR_REF_PO}</li>
+ * <li>{@link #REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_8B_HDR_OEM DOLBY_VISION_8B_HDR_OEM}</li>
+ * <li>{@link #REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_8B_HDR_OEM_PO DOLBY_VISION_8B_HDR_OEM_PO}</li>
+ * <li>{@link #REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_MAX MAX}</li>
+ * </ul>
+ *
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ * @see #REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_STANDARD
+ * @see #REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_HLG10
+ * @see #REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_HDR10
+ * @see #REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_HDR10_PLUS
+ * @see #REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_10B_HDR_REF
+ * @see #REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_10B_HDR_REF_PO
+ * @see #REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_10B_HDR_OEM
+ * @see #REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_10B_HDR_OEM_PO
+ * @see #REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_8B_HDR_REF
+ * @see #REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_8B_HDR_REF_PO
+ * @see #REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_8B_HDR_OEM
+ * @see #REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_8B_HDR_OEM_PO
+ * @see #REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_MAX
+ * @hide
+ */
+ public static final Key<int[]> REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP =
+ new Key<int[]>("android.request.availableDynamicRangeProfilesMap", int[].class);
+
+ /**
+ * <p>Recommended 10-bit dynamic range profile.</p>
+ * <p>Devices supporting the 10-bit output capability
+ * {@link android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT }
+ * must list a 10-bit supported dynamic range profile that is expected to perform
+ * optimally in terms of image quality, power and performance.
+ * The value advertised can be used as a hint by camera clients when configuring the dynamic
+ * range profile when calling
+ * {@link android.hardware.camera2.params.OutputConfiguration#setDynamicRangeProfile }.</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ */
+ @PublicKey
+ @NonNull
+ public static final Key<Integer> REQUEST_RECOMMENDED_TEN_BIT_DYNAMIC_RANGE_PROFILE =
+ new Key<Integer>("android.request.recommendedTenBitDynamicRangeProfile", int.class);
+
+ /**
* <p>The list of image formats that are supported by this
* camera device for output streams.</p>
* <p>All camera devices will support JPEG and YUV_420_888 formats.</p>
@@ -3340,6 +3422,32 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
new Key<android.hardware.camera2.params.MandatoryStreamCombination[]>("android.scaler.mandatoryMaximumResolutionStreamCombinations", android.hardware.camera2.params.MandatoryStreamCombination[].class);
/**
+ * <p>An array of mandatory stream combinations which are applicable when device support the
+ * 10-bit output capability
+ * {@link android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT }
+ * This is an app-readable conversion of the maximum resolution mandatory stream combination
+ * {@link android.hardware.camera2.CameraDevice#createCaptureSession tables}.</p>
+ * <p>The array of
+ * {@link android.hardware.camera2.params.MandatoryStreamCombination combinations} is
+ * generated according to the documented
+ * {@link android.hardware.camera2.CameraDevice#createCaptureSession guideline} for each
+ * device which has the
+ * {@link android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT }
+ * capability.
+ * Clients can use the array as a quick reference to find an appropriate camera stream
+ * combination.
+ * The mandatory stream combination array will be {@code null} in case the device is not an
+ * {@link android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT }
+ * device.</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ */
+ @PublicKey
+ @NonNull
+ @SyntheticKey
+ public static final Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_TEN_BIT_OUTPUT_STREAM_COMBINATIONS =
+ new Key<android.hardware.camera2.params.MandatoryStreamCombination[]>("android.scaler.mandatoryTenBitOutputStreamCombinations", android.hardware.camera2.params.MandatoryStreamCombination[].class);
+
+ /**
* <p>Whether the camera device supports multi-resolution input or output streams</p>
* <p>A logical multi-camera or an ultra high resolution camera may support multi-resolution
* input or output streams. With multi-resolution output streams, the camera device is able
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index 3c1ec3e629a9..47eb79d07469 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -402,7 +402,9 @@ public abstract class CameraDevice implements AutoCloseable {
* registered surfaces do not meet the device-specific
* extension requirements such as dimensions and/or
* (output format)/(surface type), or if the extension is not
- * supported.
+ * supported, or if any of the output configurations select
+ * a dynamic range different from
+ * {@link android.hardware.camera2.params.DynamicRangeProfiles#STANDARD}
* @see CameraExtensionCharacteristics#getSupportedExtensions
* @see CameraExtensionCharacteristics#getExtensionSupportedSizes
*/
@@ -822,7 +824,36 @@ public abstract class CameraDevice implements AutoCloseable {
* be chosen from. {@code DEFAULT} refers to the default sensor pixel mode {@link
* StreamConfigurationMap} and {@code MAX_RES} refers to the maximum resolution {@link
* StreamConfigurationMap}. The same capture request must not mix targets from
- * {@link StreamConfigurationMap}s corresponding to different sensor pixel modes.
+ * {@link StreamConfigurationMap}s corresponding to different sensor pixel modes. </p>
+ *
+ * <p> 10-bit output capable
+ * {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT}
+ * devices support at least the following stream combinations: </p>
+ * <table>
+ * <tr><th colspan="7">10-bit output additional guaranteed configurations</th></tr>
+ * <tr><th colspan="2" id="rb">Target 1</th><th colspan="2" id="rb">Target 2</th><th colspan="2" id="rb">Target 3</th> <th rowspan="2">Sample use case(s)</th> </tr>
+ * <tr><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th></tr>
+ * <tr> <td>{@code PRIV}</td><td id="rb">{@code MAXIMUM}</td> }</td> <td colspan="4" id="rb"></td> <td>Simple preview, GPU video processing, or no-preview video recording.</td> </tr>
+ * <tr> <td>{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> }</td> <td colspan="4" id="rb"></td> <td>In-application video/image processing.</td> </tr>
+ * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code JPEG}</td><td id="rb">{@code MAXIMUM }</td> <td colspan="2" id="rb"></td> <td>Standard still imaging.</td> </tr>
+ * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV }</td><td id="rb">{@code MAXIMUM }</td> <td colspan="2" id="rb"></td> <td>Maximum-resolution in-app processing with preview.</td> </tr>
+ * <tr> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code MAXIMUM }</td> <td colspan="2" id="rb"></td> <td>Maximum-resolution two-input in-app processing.</td> </tr>
+ * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code PRIV}</td><td id="rb">{@code RECORD }</td> <td colspan="2" id="rb"></td> <td>High-resolution video recording with preview.</td> </tr>
+ * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code PRIV}</td><td id="rb">{@code RECORD }</td> <td>{@code YUV}</td><td id="rb">{@code RECORD }</td> <td>High-resolution recording with in-app snapshot.</td> </tr>
+ * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code PRIV }</td><td id="rb">{@code RECORD }</td> <td>{@code JPEG}</td><td id="rb">{@code RECORD }</td> <td>High-resolution recording with video snapshot.</td> </tr>
+ * </table><br>
+ * <p>Here PRIV can be either 8 or 10-bit {@link android.graphics.ImageFormat#PRIVATE} pixel
+ * format. YUV can be either {@link android.graphics.ImageFormat#YUV_420_888} or
+ * {@link android.graphics.ImageFormat#YCBCR_P010}.
+ * For the maximum size column, PREVIEW refers to the best size match to the device's screen
+ * resolution, or to 1080p (1920x1080), whichever is smaller. RECORD refers to the camera
+ * device's maximum supported recording resolution, as determined by
+ * {@link android.media.CamcorderProfile}. MAXIMUM refers to the camera device's maximum output
+ * resolution for that format or target from {@link StreamConfigurationMap#getOutputSizes(int)}.
+ * Do note that invalid combinations such as having a camera surface configured to use pixel
+ * format {@link android.graphics.ImageFormat#YUV_420_888} with a 10-bit profile
+ * will cause a capture session initialization failure.
+ * </p>
*
* <p>Since the capabilities of camera devices vary greatly, a given camera device may support
* target combinations with sizes outside of these guarantees, but this can only be tested for
@@ -907,6 +938,13 @@ public abstract class CameraDevice implements AutoCloseable {
* guaranteed output targets that can be submitted in a regular or reprocess
* {@link CaptureRequest} simultaneously.</p>
*
+ * <p>Reprocessing with 10-bit output targets on 10-bit capable
+ * {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT} devices is
+ * not supported. Trying to initialize a repreocessable capture session with one ore more
+ * output configurations set {@link OutputConfiguration#setDynamicRangeProfile(int)} to use
+ * a 10-bit dynamic range profile {@link android.hardware.camera2.params.DynamicRangeProfiles}
+ * will trigger {@link IllegalArgumentException}.</p>
+ *
* <style scoped>
* #rb { border-right-width: thick; }
* </style>
@@ -1083,13 +1121,17 @@ public abstract class CameraDevice implements AutoCloseable {
*
* @throws IllegalArgumentException In case the session configuration is invalid; or the output
* configurations are empty; or the session configuration
- * executor is invalid.
+ * executor is invalid;
+ * or the output dynamic range combination is
+ * invalid/unsupported.
* @throws CameraAccessException In case the camera device is no longer connected or has
* encountered a fatal error.
* @see #createCaptureSession(List, CameraCaptureSession.StateCallback, Handler)
* @see #createCaptureSessionByOutputConfigurations
* @see #createReprocessableCaptureSession
* @see #createConstrainedHighSpeedCaptureSession
+ * @see OutputConfiguration#setDynamicRangeProfile(int)
+ * @see android.hardware.camera2.params.DynamicRangeProfiles
*/
public void createCaptureSession(
SessionConfiguration config) throws CameraAccessException {
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index 639abe9d1abf..803684da6ddb 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -1190,6 +1190,135 @@ public abstract class CameraMetadata<TKey> {
*/
public static final int REQUEST_AVAILABLE_CAPABILITIES_REMOSAIC_REPROCESSING = 17;
+ /**
+ * <p>The device supports one or more 10-bit camera outputs according to the dynamic range
+ * profiles specified in
+ * {@link android.hardware.camera2.params.DynamicRangeProfiles#getSupportedProfiles }.
+ * They can be configured as part of the capture session initialization via
+ * {@link android.hardware.camera2.params.OutputConfiguration#setDynamicRangeProfile }.
+ * Cameras that enable this capability must also support the following:
+ * * Profile {@link android.hardware.camera2.params.DynamicRangeProfiles#HLG10 }
+ * * All mandatory stream combinations for this specific capability as per
+ * documentation {@link android.hardware.camera2.CameraDevice#createCaptureSession }
+ * * In case the device is not able to capture some combination of supported
+ * standard 8-bit and/or 10-bit dynamic range profiles within the same capture request,
+ * then those constraints must be listed in
+ * {@link android.hardware.camera2.params.DynamicRangeProfiles#getProfileCaptureRequestConstraints }
+ * * Recommended dynamic range profile listed in
+ * {@link android.hardware.camera2.CameraCharacteristics#REQUEST_RECOMMENDED_TEN_BIT_DYNAMIC_RANGE_PROFILE }.</p>
+ * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
+ */
+ public static final int REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT = 18;
+
+ //
+ // Enumeration values for CameraCharacteristics#REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP
+ //
+
+ /**
+ * <p>8-bit SDR profile which is the default for all non 10-bit output capable devices.</p>
+ * @see CameraCharacteristics#REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP
+ * @hide
+ */
+ public static final int REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_STANDARD = 0x1;
+
+ /**
+ * <p>10-bit pixel samples encoded using the Hybrid log-gamma transfer function.</p>
+ * @see CameraCharacteristics#REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP
+ * @hide
+ */
+ public static final int REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_HLG10 = 0x2;
+
+ /**
+ * <p>10-bit pixel samples encoded using the SMPTE ST 2084 transfer function.
+ * This profile utilizes internal static metadata to increase the quality
+ * of the capture.</p>
+ * @see CameraCharacteristics#REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP
+ * @hide
+ */
+ public static final int REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_HDR10 = 0x4;
+
+ /**
+ * <p>10-bit pixel samples encoded using the SMPTE ST 2084 transfer function.
+ * In contrast to HDR10, this profile uses internal per-frame metadata
+ * to further enhance the quality of the capture.</p>
+ * @see CameraCharacteristics#REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP
+ * @hide
+ */
+ public static final int REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_HDR10_PLUS = 0x8;
+
+ /**
+ * <p>This is a camera mode for Dolby Vision capture optimized for a more scene
+ * accurate capture. This would typically differ from what a specific device
+ * might want to tune for a consumer optimized Dolby Vision general capture.</p>
+ * @see CameraCharacteristics#REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP
+ * @hide
+ */
+ public static final int REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_10B_HDR_REF = 0x10;
+
+ /**
+ * <p>This is the power optimized mode for 10-bit Dolby Vision HDR Reference Mode.</p>
+ * @see CameraCharacteristics#REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP
+ * @hide
+ */
+ public static final int REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_10B_HDR_REF_PO = 0x20;
+
+ /**
+ * <p>This is the camera mode for the default Dolby Vision capture mode for the
+ * specific device. This would be tuned by each specific device for consumer
+ * pleasing results that resonate with their particular audience. We expect
+ * that each specific device would have a different look for their default
+ * Dolby Vision capture.</p>
+ * @see CameraCharacteristics#REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP
+ * @hide
+ */
+ public static final int REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_10B_HDR_OEM = 0x40;
+
+ /**
+ * <p>This is the power optimized mode for 10-bit Dolby Vision HDR device specific
+ * capture Mode.</p>
+ * @see CameraCharacteristics#REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP
+ * @hide
+ */
+ public static final int REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_10B_HDR_OEM_PO = 0x80;
+
+ /**
+ * <p>This is the 8-bit version of the Dolby Vision reference capture mode optimized
+ * for scene accuracy.</p>
+ * @see CameraCharacteristics#REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP
+ * @hide
+ */
+ public static final int REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_8B_HDR_REF = 0x100;
+
+ /**
+ * <p>This is the power optimized mode for 8-bit Dolby Vision HDR Reference Mode.</p>
+ * @see CameraCharacteristics#REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP
+ * @hide
+ */
+ public static final int REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_8B_HDR_REF_PO = 0x200;
+
+ /**
+ * <p>This is the 8-bit version of device specific tuned and optimized Dolby Vision
+ * capture mode.</p>
+ * @see CameraCharacteristics#REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP
+ * @hide
+ */
+ public static final int REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_8B_HDR_OEM = 0x400;
+
+ /**
+ * <p>This is the power optimized mode for 8-bit Dolby Vision HDR device specific
+ * capture Mode.</p>
+ * @see CameraCharacteristics#REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP
+ * @hide
+ */
+ public static final int REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_8B_HDR_OEM_PO = 0x800;
+
+ /**
+ *
+ * @see CameraCharacteristics#REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP
+ * @hide
+ */
+ public static final int REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_MAX = 0x1000;
+
//
// Enumeration values for CameraCharacteristics#SCALER_CROPPING_TYPE
//
diff --git a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
index b8443fb6d14b..9d2c901ed049 100644
--- a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
@@ -35,7 +35,6 @@ import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.TotalCaptureResult;
import android.hardware.camera2.extension.CameraOutputConfig;
import android.hardware.camera2.extension.CameraSessionConfig;
-import android.hardware.camera2.extension.CaptureStageImpl;
import android.hardware.camera2.extension.IAdvancedExtenderImpl;
import android.hardware.camera2.extension.ICaptureCallback;
import android.hardware.camera2.extension.IImageProcessorImpl;
@@ -49,6 +48,7 @@ import android.hardware.camera2.extension.ParcelCaptureResult;
import android.hardware.camera2.extension.ParcelImage;
import android.hardware.camera2.extension.ParcelTotalCaptureResult;
import android.hardware.camera2.extension.Request;
+import android.hardware.camera2.params.DynamicRangeProfiles;
import android.hardware.camera2.params.ExtensionSessionConfiguration;
import android.hardware.camera2.params.OutputConfiguration;
import android.hardware.camera2.params.SessionConfiguration;
@@ -130,6 +130,13 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes
config.getOutputConfigurations().size() + " expected <= 2");
}
+ for (OutputConfiguration c : config.getOutputConfigurations()) {
+ if (c.getDynamicRangeProfile() != DynamicRangeProfiles.STANDARD) {
+ throw new IllegalArgumentException("Unsupported dynamic range profile: " +
+ c.getDynamicRangeProfile());
+ }
+ }
+
int suitableSurfaceCount = 0;
List<Size> supportedPreviewSizes = extensionChars.getExtensionSupportedSizes(
config.getExtension(), SurfaceTexture.class);
diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
index 71047af69b87..c8ecfd0bdea9 100644
--- a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
@@ -39,6 +39,7 @@ import android.hardware.camera2.extension.IPreviewExtenderImpl;
import android.hardware.camera2.extension.IRequestUpdateProcessorImpl;
import android.hardware.camera2.extension.ParcelImage;
import android.hardware.camera2.TotalCaptureResult;
+import android.hardware.camera2.params.DynamicRangeProfiles;
import android.hardware.camera2.params.ExtensionSessionConfiguration;
import android.hardware.camera2.params.OutputConfiguration;
import android.hardware.camera2.params.SessionConfiguration;
@@ -145,6 +146,13 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
config.getOutputConfigurations().size() + " expected <= 2");
}
+ for (OutputConfiguration c : config.getOutputConfigurations()) {
+ if (c.getDynamicRangeProfile() != DynamicRangeProfiles.STANDARD) {
+ throw new IllegalArgumentException("Unsupported dynamic range profile: " +
+ c.getDynamicRangeProfile());
+ }
+ }
+
Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders =
CameraExtensionCharacteristics.initializeExtension(config.getExtension());
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index e393a66eb733..0f8bdf64e132 100644
--- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -51,6 +51,7 @@ import android.hardware.camera2.marshal.impl.MarshalQueryableStreamConfiguration
import android.hardware.camera2.marshal.impl.MarshalQueryableString;
import android.hardware.camera2.params.Capability;
import android.hardware.camera2.params.DeviceStateSensorOrientationMap;
+import android.hardware.camera2.params.DynamicRangeProfiles;
import android.hardware.camera2.params.Face;
import android.hardware.camera2.params.HighSpeedVideoConfiguration;
import android.hardware.camera2.params.LensShadingMap;
@@ -331,6 +332,7 @@ public class CameraMetadataNative implements Parcelable {
private static final int MANDATORY_STREAM_CONFIGURATIONS_DEFAULT = 0;
private static final int MANDATORY_STREAM_CONFIGURATIONS_MAX_RESOLUTION = 1;
private static final int MANDATORY_STREAM_CONFIGURATIONS_CONCURRENT = 2;
+ private static final int MANDATORY_STREAM_CONFIGURATIONS_10BIT = 3;
private static String translateLocationProviderToProcess(final String provider) {
if (provider == null) {
@@ -678,6 +680,16 @@ public class CameraMetadataNative implements Parcelable {
});
sGetCommandMap.put(
+ CameraCharacteristics.SCALER_MANDATORY_TEN_BIT_OUTPUT_STREAM_COMBINATIONS.getNativeKey(),
+ new GetCommand() {
+ @Override
+ @SuppressWarnings("unchecked")
+ public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
+ return (T) metadata.getMandatory10BitStreamCombinations();
+ }
+ });
+
+ sGetCommandMap.put(
CameraCharacteristics.SCALER_MANDATORY_MAXIMUM_RESOLUTION_STREAM_COMBINATIONS.getNativeKey(),
new GetCommand() {
@Override
@@ -771,6 +783,15 @@ public class CameraMetadataNative implements Parcelable {
}
});
sGetCommandMap.put(
+ CameraCharacteristics.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES.getNativeKey(),
+ new GetCommand() {
+ @Override
+ @SuppressWarnings("unchecked")
+ public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
+ return (T) metadata.getDynamicRangeProfiles();
+ }
+ });
+ sGetCommandMap.put(
CaptureResult.STATISTICS_OIS_SAMPLES.getNativeKey(),
new GetCommand() {
@Override
@@ -1015,6 +1036,17 @@ public class CameraMetadataNative implements Parcelable {
return map;
}
+ private DynamicRangeProfiles getDynamicRangeProfiles() {
+ int[] profileArray = getBase(
+ CameraCharacteristics.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP);
+
+ if (profileArray == null) {
+ return null;
+ }
+
+ return new DynamicRangeProfiles(profileArray);
+ }
+
private Location getGpsLocation() {
String processingMethod = get(CaptureResult.JPEG_GPS_PROCESSING_METHOD);
double[] coords = get(CaptureResult.JPEG_GPS_COORDINATES);
@@ -1378,6 +1410,9 @@ public class CameraMetadataNative implements Parcelable {
case MANDATORY_STREAM_CONFIGURATIONS_MAX_RESOLUTION:
combs = build.getAvailableMandatoryMaximumResolutionStreamCombinations();
break;
+ case MANDATORY_STREAM_CONFIGURATIONS_10BIT:
+ combs = build.getAvailableMandatory10BitStreamCombinations();
+ break;
default:
combs = build.getAvailableMandatoryStreamCombinations();
}
@@ -1389,6 +1424,10 @@ public class CameraMetadataNative implements Parcelable {
return null;
}
+ private MandatoryStreamCombination[] getMandatory10BitStreamCombinations() {
+ return getMandatoryStreamCombinationsHelper(MANDATORY_STREAM_CONFIGURATIONS_10BIT);
+ }
+
private MandatoryStreamCombination[] getMandatoryConcurrentStreamCombinations() {
if (!mHasMandatoryConcurrentStreams) {
return null;
diff --git a/core/java/android/hardware/camera2/params/DynamicRangeProfiles.java b/core/java/android/hardware/camera2/params/DynamicRangeProfiles.java
new file mode 100644
index 000000000000..5c1a4aa120ea
--- /dev/null
+++ b/core/java/android/hardware/camera2/params/DynamicRangeProfiles.java
@@ -0,0 +1,301 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.camera2.params;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+
+import android.hardware.camera2.CameraMetadata;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Immutable class with information about supported 10-bit dynamic range profiles.
+ *
+ * <p>An instance of this class can be queried by retrieving the value of
+ * {@link android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES}.
+ * </p>
+ *
+ * <p>All camera devices supporting the
+ * {@link android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT}
+ * capability must advertise the supported 10-bit dynamic range profiles in
+ * {@link #getSupportedProfiles}</p>
+ *
+ * <p>Some devices may not be able to support 8-bit and/or 10-bit output with different dynamic
+ * range profiles within the same capture request. Such device specific constraints can be queried
+ * by calling {@link #getProfileCaptureRequestConstraints(int)}. Do note that unsupported
+ * combinations will result in {@link IllegalArgumentException} when trying to submit a capture
+ * request. Capture requests that only reference outputs configured using the same dynamic range
+ * profile value will never fail due to such constraints.</p>
+ *
+ * @see OutputConfiguration#setDynamicRangeProfile(int)
+ */
+public final class DynamicRangeProfiles {
+ /**
+ * This the default 8-bit standard profile that will be used in case where camera clients do not
+ * explicitly configure a supported dynamic range profile by calling
+ * {@link OutputConfiguration#setDynamicRangeProfile(int)}.
+ */
+ public static final int STANDARD =
+ CameraMetadata.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_STANDARD;
+
+ /**
+ * 10-bit pixel samples encoded using the Hybrid log-gamma transfer function
+ *
+ * <p>All 10-bit output capable devices are required to support this profile.</p>
+ */
+ public static final int HLG10 =
+ CameraMetadata.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_HLG10;
+
+ /**
+ * 10-bit pixel samples encoded using the SMPTE ST 2084 transfer function.
+ *
+ * <p>This profile utilizes internal static metadata to increase the quality
+ * of the capture.</p>
+ */
+ public static final int HDR10 =
+ CameraMetadata.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_HDR10;
+
+ /**
+ * 10-bit pixel samples encoded using the SMPTE ST 2084 transfer function.
+ *
+ * <p>In contrast to HDR10, this profile uses internal per-frame metadata
+ * to further enhance the quality of the capture.</p>
+ */
+ public static final int HDR10_PLUS =
+ CameraMetadata.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_HDR10_PLUS;
+
+ /**
+ * <p>This is a camera mode for Dolby Vision capture optimized for a more scene
+ * accurate capture. This would typically differ from what a specific device
+ * might want to tune for a consumer optimized Dolby Vision general capture.</p>
+ */
+ public static final int DOLBY_VISION_10B_HDR_REF =
+ CameraMetadata.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_10B_HDR_REF;
+
+ /**
+ * <p>This is the power optimized mode for 10-bit Dolby Vision HDR Reference Mode.</p>
+ */
+ public static final int DOLBY_VISION_10B_HDR_REF_PO =
+ CameraMetadata.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_10B_HDR_REF_PO;
+
+ /**
+ * <p>This is the camera mode for the default Dolby Vision capture mode for the
+ * specific device. This would be tuned by each specific device for consumer
+ * pleasing results that resonate with their particular audience. We expect
+ * that each specific device would have a different look for their default
+ * Dolby Vision capture.</p>
+ */
+ public static final int DOLBY_VISION_10B_HDR_OEM =
+ CameraMetadata.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_10B_HDR_OEM;
+
+ /**
+ * <p>This is the power optimized mode for 10-bit Dolby Vision HDR device specific capture
+ * Mode.</p>
+ */
+ public static final int DOLBY_VISION_10B_HDR_OEM_PO =
+ CameraMetadata.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_10B_HDR_OEM_PO;
+
+ /**
+ * <p>This is the 8-bit version of the Dolby Vision reference capture mode optimized
+ * for scene accuracy.</p>
+ */
+ public static final int DOLBY_VISION_8B_HDR_REF =
+ CameraMetadata.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_8B_HDR_REF;
+
+ /**
+ * <p>This is the power optimized mode for 8-bit Dolby Vision HDR Reference Mode.</p>
+ */
+ public static final int DOLBY_VISION_8B_HDR_REF_PO =
+ CameraMetadata.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_8B_HDR_REF_PO;
+
+ /**
+ * <p>This is the 8-bit version of device specific tuned and optimized Dolby Vision
+ * capture mode.</p>
+ */
+ public static final int DOLBY_VISION_8B_HDR_OEM =
+ CameraMetadata.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_8B_HDR_OEM;
+
+ /**
+ * <p>This is the power optimized mode for 8-bit Dolby Vision HDR device specific
+ * capture Mode.</p>
+ */
+ public static final int DOLBY_VISION_8B_HDR_OEM_PO =
+ CameraMetadata.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_8B_HDR_OEM_PO;
+
+ /*
+ * @hide
+ */
+ public static final int PUBLIC_MAX =
+ CameraMetadata.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_MAX;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"PROFILE_"}, value =
+ {STANDARD,
+ HLG10,
+ HDR10,
+ HDR10_PLUS,
+ DOLBY_VISION_10B_HDR_REF,
+ DOLBY_VISION_10B_HDR_REF_PO,
+ DOLBY_VISION_10B_HDR_OEM,
+ DOLBY_VISION_10B_HDR_OEM_PO,
+ DOLBY_VISION_8B_HDR_REF,
+ DOLBY_VISION_8B_HDR_REF_PO,
+ DOLBY_VISION_8B_HDR_OEM,
+ DOLBY_VISION_8B_HDR_OEM_PO})
+ public @interface Profile {
+ }
+
+ private final HashMap<Integer, Set<Integer>> mProfileMap = new HashMap<>();
+
+ /**
+ * Create a new immutable DynamicRangeProfiles instance.
+ *
+ * <p>This constructor takes over the array; do not write to the array afterwards.</p>
+ *
+ * <p>Do note that the constructor is available for testing purposes only!
+ * Camera clients must always retrieve the value of
+ * {@link android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES}.
+ * for a given camera id in order to retrieve the device capabilities.</p>
+ *
+ * @param elements
+ * An array of elements describing the map. It contains two elements per entry which
+ * describe the supported dynamic range profile value in the first element and in the
+ * second element a bitmap of concurrently supported dynamic range profiles within the
+ * same capture request. Bitmap values of 0 indicate that there are no constraints.
+ *
+ * @throws IllegalArgumentException
+ * if the {@code elements} array length is invalid, not divisible by 2 or contains
+ * invalid element values
+ * @throws NullPointerException
+ * if {@code elements} is {@code null}
+ *
+ */
+ public DynamicRangeProfiles(@NonNull final int[] elements) {
+ if ((elements.length % 2) != 0) {
+ throw new IllegalArgumentException("Dynamic range profile map length " +
+ elements.length + " is not even!");
+ }
+
+ for (int i = 0; i < elements.length; i += 2) {
+ checkProfileValue(elements[i]);
+ // STANDARD is not expected to be included
+ if (elements[i] == STANDARD) {
+ throw new IllegalArgumentException("Dynamic range profile map must not include a"
+ + " STANDARD profile entry!");
+ }
+ HashSet<Integer> profiles = new HashSet<>();
+
+ if (elements[i+1] != 0) {
+ for (int profile = STANDARD; profile < PUBLIC_MAX; profile <<= 1) {
+ if ((elements[i+1] & profile) != 0) {
+ profiles.add(profile);
+ }
+ }
+ }
+
+ mProfileMap.put(elements[i], profiles);
+ }
+
+ // Build the STANDARD constraints depending on the advertised 10-bit limitations
+ HashSet<Integer> standardConstraints = new HashSet<>();
+ standardConstraints.add(STANDARD);
+ for(Integer profile : mProfileMap.keySet()) {
+ if (mProfileMap.get(profile).isEmpty() || mProfileMap.get(profile).contains(STANDARD)) {
+ standardConstraints.add(profile);
+ }
+ }
+
+ mProfileMap.put(STANDARD, standardConstraints);
+ }
+
+
+ /**
+ * @hide
+ */
+ public static void checkProfileValue(int profile) {
+ switch (profile) {
+ case STANDARD:
+ case HLG10:
+ case HDR10:
+ case HDR10_PLUS:
+ case DOLBY_VISION_10B_HDR_REF:
+ case DOLBY_VISION_10B_HDR_REF_PO:
+ case DOLBY_VISION_10B_HDR_OEM:
+ case DOLBY_VISION_10B_HDR_OEM_PO:
+ case DOLBY_VISION_8B_HDR_REF:
+ case DOLBY_VISION_8B_HDR_REF_PO:
+ case DOLBY_VISION_8B_HDR_OEM:
+ case DOLBY_VISION_8B_HDR_OEM_PO:
+ //No-op
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown profile " + profile);
+ }
+ }
+
+ /**
+ * Return a set of supported dynamic range profiles.
+ *
+ * @return non-modifiable set of dynamic range profiles
+ */
+ public @NonNull Set<Integer> getSupportedProfiles() {
+ return Collections.unmodifiableSet(mProfileMap.keySet());
+ }
+
+ /**
+ * Return a list of supported dynamic range profiles that
+ * can be referenced in a single capture request along with a given
+ * profile.
+ *
+ * <p>For example if assume that a particular 10-bit output capable device
+ * returns ({@link #STANDARD}, {@link #HLG10}, {@link #HDR10}) as result from calling
+ * {@link #getSupportedProfiles()} and {@link #getProfileCaptureRequestConstraints(int)}
+ * returns ({@link #STANDARD}, {@link #HLG10}) when given an argument of {@link #STANDARD}.
+ * This means that the corresponding camera device will only accept and process capture requests
+ * that reference outputs configured using {@link #HDR10} dynamic profile or alternatively
+ * some combination of {@link #STANDARD} and {@link #HLG10}. However trying to
+ * queue capture requests to outputs that reference both {@link #HDR10} and
+ * {@link #STANDARD}/{@link #HLG10} will result in {@link IllegalArgumentException}.</p>
+ *
+ * <p>The list will be empty in case there are no constraints for the given
+ * profile.</p>
+ *
+ * @return non-modifiable set of dynamic range profiles
+ * @throws IllegalArgumentException - If the profile argument is not
+ * within the list returned by
+ * getSupportedProfiles()
+ *
+ * @see OutputConfiguration#setDynamicRangeProfile(int)
+ */
+ public @NonNull Set<Integer> getProfileCaptureRequestConstraints(@Profile int profile) {
+ Set<Integer> ret = mProfileMap.get(profile);
+ if (ret == null) {
+ throw new IllegalArgumentException("Unsupported profile!");
+ }
+
+ return Collections.unmodifiableSet(ret);
+ }
+}
diff --git a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
index a6789213b50b..32c15da4a909 100644
--- a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
+++ b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
@@ -18,8 +18,6 @@ package android.hardware.camera2.params;
import static android.hardware.camera2.params.StreamConfigurationMap.checkArgumentFormat;
-import static com.android.internal.util.Preconditions.*;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.ImageFormat;
@@ -28,7 +26,6 @@ import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CameraMetadata;
-import android.hardware.camera2.params.StreamConfigurationMap;
import android.hardware.camera2.utils.HashCodeHelpers;
import android.media.CamcorderProfile;
import android.util.Log;
@@ -40,6 +37,7 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
/**
@@ -64,6 +62,7 @@ public final class MandatoryStreamCombination {
private final boolean mIsInput;
private final boolean mIsUltraHighResolution;
private final boolean mIsMaximumSize;
+ private final boolean mIs10BitCapable;
/**
* Create a new {@link MandatoryStreamInformation}.
@@ -119,6 +118,29 @@ public final class MandatoryStreamCombination {
*/
public MandatoryStreamInformation(@NonNull List<Size> availableSizes, @Format int format,
boolean isMaximumSize, boolean isInput, boolean isUltraHighResolution) {
+ this(availableSizes, format, isMaximumSize, isInput, isUltraHighResolution,
+ /*is10bitCapable*/ false);
+ }
+
+ /**
+ * Create a new {@link MandatoryStreamInformation}.
+ *
+ * @param availableSizes List of possible stream sizes.
+ * @param format Image format.
+ * @param isMaximumSize Whether this is a maximum size stream.
+ * @param isInput Flag indicating whether this stream is input.
+ * @param isUltraHighResolution Flag indicating whether this is a ultra-high resolution
+ * stream.
+ * @param is10BitCapable Flag indicating whether this stream is able to support 10-bit
+ *
+ * @throws IllegalArgumentException
+ * if sizes is empty or if the format was not user-defined in
+ * ImageFormat/PixelFormat.
+ * @hide
+ */
+ public MandatoryStreamInformation(@NonNull List<Size> availableSizes, @Format int format,
+ boolean isMaximumSize, boolean isInput, boolean isUltraHighResolution,
+ boolean is10BitCapable) {
if (availableSizes.isEmpty()) {
throw new IllegalArgumentException("No available sizes");
}
@@ -127,6 +149,7 @@ public final class MandatoryStreamCombination {
mIsMaximumSize = isMaximumSize;
mIsInput = isInput;
mIsUltraHighResolution = isUltraHighResolution;
+ mIs10BitCapable = is10BitCapable;
}
/**
@@ -180,6 +203,27 @@ public final class MandatoryStreamCombination {
}
/**
+ * Indicates whether this stream is able to support 10-bit output.
+ *
+ * <p>10-bit capable streams can be configured to output 10-bit sample data via calls to
+ * {@link android.hardware.camera2.params.OutputConfiguration#setDynamicRangeProfile} and
+ * selecting the appropriate output Surface pixel format which can be queried via
+ * {@link #get10BitFormat()} and will be either
+ * {@link ImageFormat#PRIVATE} (the default for Surfaces initialized by
+ * {@link android.view.SurfaceView}, {@link android.view.TextureView},
+ * {@link android.media.MediaRecorder}, {@link android.media.MediaCodec} etc.) or
+ * {@link ImageFormat#YCBCR_P010}.</p>
+ *
+ * @return true if stream is able to output 10-bit pixels
+ *
+ * @see android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT
+ * @see OutputConfiguration#setDynamicRangeProfile(int)
+ */
+ public boolean is10BitCapable() {
+ return mIs10BitCapable;
+ }
+
+ /**
* Return the list of available sizes for this mandatory stream.
*
* <p>Per documented {@link CameraDevice#createCaptureSession guideline} the largest
@@ -201,6 +245,29 @@ public final class MandatoryStreamCombination {
* @return integer format.
*/
public @Format int getFormat() {
+ // P010 YUV streams must be supported along with SDR 8-bit YUV streams
+ if ((mIs10BitCapable) && (mFormat == ImageFormat.YCBCR_P010)) {
+ return ImageFormat.YUV_420_888;
+ }
+ return mFormat;
+ }
+
+ /**
+ * Retrieve the mandatory stream 10-bit {@code format} for 10-bit capable streams.
+ *
+ * <p>In case {@link #is10BitCapable()} returns {@code true}, then this method
+ * will return the corresponding 10-bit output Surface pixel format. Depending on
+ * the stream type it will be either {@link ImageFormat#PRIVATE} or
+ * {@link ImageFormat#YCBCR_P010}.</p>
+ *
+ * @return integer format.
+ * @throws UnsupportedOperationException in case the stream is not capable of 10-bit output
+ * @see #is10BitCapable()
+ */
+ public @Format int get10BitFormat() {
+ if (!mIs10BitCapable) {
+ throw new UnsupportedOperationException("10-bit output is not supported!");
+ }
return mFormat;
}
@@ -932,6 +999,41 @@ public final class MandatoryStreamCombination {
/*reprocessType*/ ReprocessType.PRIVATE),
};
+ private static StreamCombinationTemplate s10BitOutputStreamCombinations[] = {
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.MAXIMUM)},
+ "Simple preview, GPU video processing, or no-preview video recording"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.YCBCR_P010, SizeThreshold.MAXIMUM)},
+ "In-application video/image processing"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM),
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW)},
+ "Standard still imaging"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.YCBCR_P010, SizeThreshold.MAXIMUM),
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW)},
+ "Maximum-resolution in-app processing with preview"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.YCBCR_P010, SizeThreshold.MAXIMUM),
+ new StreamTemplate(ImageFormat.YCBCR_P010, SizeThreshold.PREVIEW)},
+ "Maximum-resolution two-input in-app processing"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.RECORD),
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW)},
+ "High-resolution video recording with preview"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.YCBCR_P010, SizeThreshold.RECORD),
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.RECORD),
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW)},
+ "High-resolution recording with in-app snapshot"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.JPEG, SizeThreshold.RECORD),
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.RECORD),
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW)},
+ "High-resolution recording with video snapshot"),
+ };
+
/**
* Helper builder class to generate a list of available mandatory stream combinations.
* @hide
@@ -971,6 +1073,86 @@ public final class MandatoryStreamCombination {
}
/**
+ * Retrieve a list of all available mandatory 10-bit output capable stream combinations.
+ *
+ * @return a non-modifiable list of supported mandatory 10-bit capable stream combinations,
+ * null in case device is not 10-bit output capable.
+ */
+ public @NonNull List<MandatoryStreamCombination>
+ getAvailableMandatory10BitStreamCombinations() {
+ // Since 10-bit streaming support is optional, we mandate these stream
+ // combinations regardless of camera device capabilities.
+
+ StreamCombinationTemplate []chosenStreamCombinations = s10BitOutputStreamCombinations;
+ if (!is10BitOutputSupported()) {
+ Log.v(TAG, "Device is not able to output 10-bit!");
+ return null;
+ }
+
+ HashMap<Pair<SizeThreshold, Integer>, List<Size>> availableSizes =
+ enumerateAvailableSizes();
+ if (availableSizes == null) {
+ Log.e(TAG, "Available size enumeration failed!");
+ return null;
+ }
+
+ ArrayList<MandatoryStreamCombination> availableStreamCombinations = new ArrayList<>();
+ availableStreamCombinations.ensureCapacity(chosenStreamCombinations.length);
+ for (StreamCombinationTemplate combTemplate : chosenStreamCombinations) {
+ ArrayList<MandatoryStreamInformation> streamsInfo = new ArrayList<>();
+ streamsInfo.ensureCapacity(combTemplate.mStreamTemplates.length);
+ for (StreamTemplate template : combTemplate.mStreamTemplates) {
+ List<Size> sizes = null;
+ Pair<SizeThreshold, Integer> pair;
+ pair = new Pair<>(template.mSizeThreshold, new Integer(template.mFormat));
+ sizes = availableSizes.get(pair);
+ if (template.mFormat == ImageFormat.YCBCR_P010) {
+ // Make sure that exactly the same 10 and 8-bit YUV streams sizes are
+ // supported
+ pair = new Pair<>(template.mSizeThreshold,
+ new Integer(ImageFormat.YUV_420_888));
+ HashSet<Size> sdrYuvSizes = new HashSet<>(availableSizes.get(pair));
+ if (!sdrYuvSizes.equals(new HashSet<>(sizes))) {
+ Log.e(TAG, "The supported 10-bit YUV sizes are different from the"
+ + " supported 8-bit YUV sizes!");
+ return null;
+ }
+ }
+
+ MandatoryStreamInformation streamInfo;
+ boolean isMaximumSize =
+ (template.mSizeThreshold == SizeThreshold.MAXIMUM);
+ try {
+ streamInfo = new MandatoryStreamInformation(sizes, template.mFormat,
+ isMaximumSize, /*isInput*/ false,
+ /*isUltraHighResolution*/ false,
+ /*is10BitCapable*/ template.mFormat != ImageFormat.JPEG);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "No available sizes found for format: " + template.mFormat +
+ " size threshold: " + template.mSizeThreshold + " combination: " +
+ combTemplate.mDescription);
+ return null;
+ }
+ streamsInfo.add(streamInfo);
+ }
+
+ MandatoryStreamCombination streamCombination;
+ try {
+ streamCombination = new MandatoryStreamCombination(streamsInfo,
+ combTemplate.mDescription, /*isReprocessable*/ false);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "No stream information for mandatory combination: "
+ + combTemplate.mDescription);
+ return null;
+ }
+
+ availableStreamCombinations.add(streamCombination);
+ }
+
+ return Collections.unmodifiableList(availableStreamCombinations);
+ }
+
+ /**
* Retrieve a list of all available mandatory concurrent stream combinations.
* This method should only be called for devices which are listed in combinations returned
* by CameraManager.getConcurrentCameraIds.
@@ -1444,7 +1626,8 @@ public final class MandatoryStreamCombination {
final int[] formats = {
ImageFormat.PRIVATE,
ImageFormat.YUV_420_888,
- ImageFormat.JPEG
+ ImageFormat.JPEG,
+ ImageFormat.YCBCR_P010
};
Size recordingMaxSize = new Size(0, 0);
Size previewMaxSize = new Size(0, 0);
@@ -1464,7 +1647,11 @@ public final class MandatoryStreamCombination {
HashMap<Integer, Size[]> allSizes = new HashMap<Integer, Size[]>();
for (int format : formats) {
Integer intFormat = new Integer(format);
- allSizes.put(intFormat, mStreamConfigMap.getOutputSizes(format));
+ Size[] sizes = mStreamConfigMap.getOutputSizes(format);
+ if (sizes == null) {
+ sizes = new Size[0];
+ }
+ allSizes.put(intFormat, sizes);
}
List<Size> previewSizes = getSizesWithinBound(
@@ -1646,6 +1833,14 @@ public final class MandatoryStreamCombination {
}
/**
+ * Check whether the current device supports 10-bit output.
+ */
+ private boolean is10BitOutputSupported() {
+ return isCapabilitySupported(
+ CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT);
+ }
+
+ /**
* Check whether the current device supports private reprocessing.
*/
private boolean isPrivateReprocessingSupported() {
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index 5bb7201eff65..f2b881ba7758 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -29,6 +29,8 @@ import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.MultiResolutionImageReader;
+import android.hardware.camera2.params.DynamicRangeProfiles;
+import android.hardware.camera2.params.DynamicRangeProfiles.Profile;
import android.hardware.camera2.params.MultiResolutionStreamInfo;
import android.hardware.camera2.utils.HashCodeHelpers;
import android.hardware.camera2.utils.SurfaceUtils;
@@ -258,6 +260,39 @@ public final class OutputConfiguration implements Parcelable {
}
/**
+ * Set a specific device supported dynamic range profile.
+ *
+ * <p>Clients can choose from any profile advertised as supported in
+ * CameraCharacteristics.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES
+ * queried using {@link DynamicRangeProfiles#getSupportedProfiles()}.
+ * If this is not explicitly set, then the default profile will be
+ * {@link DynamicRangeProfiles#STANDARD}.</p>
+ *
+ * <p>Do note that invalid combinations between the registered output
+ * surface pixel format and the configured dynamic range profile will
+ * cause capture session initialization failure. Invalid combinations
+ * include any 10-bit dynamic range profile advertised in
+ * {@link DynamicRangeProfiles#getSupportedProfiles()} combined with
+ * an output Surface pixel format different from {@link ImageFormat#PRIVATE}
+ * (the default for Surfaces initialized by {@link android.view.SurfaceView},
+ * {@link android.view.TextureView}, {@link android.media.MediaRecorder},
+ * {@link android.media.MediaCodec} etc.)
+ * or {@link ImageFormat#YCBCR_P010}.</p>
+ */
+ public void setDynamicRangeProfile(@Profile int profile) {
+ mDynamicRangeProfile = profile;
+ }
+
+ /**
+ * Return current dynamic range profile.
+ *
+ * @return the currently set dynamic range profile
+ */
+ public @Profile int getDynamicRangeProfile() {
+ return mDynamicRangeProfile;
+ }
+
+ /**
* Create a new {@link OutputConfiguration} instance.
*
* <p>This constructor takes an argument for desired camera rotation</p>
@@ -319,6 +354,7 @@ public final class OutputConfiguration implements Parcelable {
mPhysicalCameraId = null;
mIsMultiResolution = false;
mSensorPixelModesUsed = new ArrayList<Integer>();
+ mDynamicRangeProfile = DynamicRangeProfiles.STANDARD;
}
/**
@@ -416,6 +452,7 @@ public final class OutputConfiguration implements Parcelable {
mPhysicalCameraId = null;
mIsMultiResolution = false;
mSensorPixelModesUsed = new ArrayList<Integer>();
+ mDynamicRangeProfile = DynamicRangeProfiles.STANDARD;
}
/**
@@ -718,6 +755,7 @@ public final class OutputConfiguration implements Parcelable {
this.mPhysicalCameraId = other.mPhysicalCameraId;
this.mIsMultiResolution = other.mIsMultiResolution;
this.mSensorPixelModesUsed = other.mSensorPixelModesUsed;
+ this.mDynamicRangeProfile = other.mDynamicRangeProfile;
}
/**
@@ -737,6 +775,8 @@ public final class OutputConfiguration implements Parcelable {
boolean isMultiResolutionOutput = source.readInt() == 1;
int[] sensorPixelModesUsed = source.createIntArray();
checkArgumentInRange(rotation, ROTATION_0, ROTATION_270, "Rotation constant");
+ int dynamicRangeProfile = source.readInt();
+ DynamicRangeProfiles.checkProfileValue(dynamicRangeProfile);
mSurfaceGroupId = surfaceSetId;
mRotation = rotation;
@@ -760,6 +800,7 @@ public final class OutputConfiguration implements Parcelable {
mPhysicalCameraId = physicalCameraId;
mIsMultiResolution = isMultiResolutionOutput;
mSensorPixelModesUsed = convertIntArrayToIntegerList(sensorPixelModesUsed);
+ mDynamicRangeProfile = dynamicRangeProfile;
}
/**
@@ -875,6 +916,7 @@ public final class OutputConfiguration implements Parcelable {
dest.writeInt(mIsMultiResolution ? 1 : 0);
// writeList doesn't seem to work well with Integer list.
dest.writeIntArray(convertIntegerToIntList(mSensorPixelModesUsed));
+ dest.writeInt(mDynamicRangeProfile);
}
/**
@@ -920,6 +962,9 @@ public final class OutputConfiguration implements Parcelable {
if (mSurfaces.get(i) != other.mSurfaces.get(i))
return false;
}
+ if (mDynamicRangeProfile != other.mDynamicRangeProfile) {
+ return false;
+ }
return true;
}
@@ -939,7 +984,8 @@ public final class OutputConfiguration implements Parcelable {
mRotation, mConfiguredSize.hashCode(), mConfiguredFormat, mConfiguredDataspace,
mSurfaceGroupId, mSurfaceType, mIsShared ? 1 : 0,
mPhysicalCameraId == null ? 0 : mPhysicalCameraId.hashCode(),
- mIsMultiResolution ? 1 : 0, mSensorPixelModesUsed.hashCode());
+ mIsMultiResolution ? 1 : 0, mSensorPixelModesUsed.hashCode(),
+ mDynamicRangeProfile);
}
return HashCodeHelpers.hashCode(
@@ -947,7 +993,8 @@ public final class OutputConfiguration implements Parcelable {
mConfiguredSize.hashCode(), mConfiguredFormat,
mConfiguredDataspace, mSurfaceGroupId, mIsShared ? 1 : 0,
mPhysicalCameraId == null ? 0 : mPhysicalCameraId.hashCode(),
- mIsMultiResolution ? 1 : 0, mSensorPixelModesUsed.hashCode());
+ mIsMultiResolution ? 1 : 0, mSensorPixelModesUsed.hashCode(),
+ mDynamicRangeProfile);
}
private static final String TAG = "OutputConfiguration";
@@ -979,4 +1026,6 @@ public final class OutputConfiguration implements Parcelable {
private boolean mIsMultiResolution;
// The sensor pixel modes that this OutputConfiguration will use
private ArrayList<Integer> mSensorPixelModesUsed;
+ // Dynamic range profile
+ private int mDynamicRangeProfile;
}
diff --git a/core/java/android/hardware/camera2/params/RecommendedStreamConfigurationMap.java b/core/java/android/hardware/camera2/params/RecommendedStreamConfigurationMap.java
index 2d725989af17..80db38fc9d8f 100644
--- a/core/java/android/hardware/camera2/params/RecommendedStreamConfigurationMap.java
+++ b/core/java/android/hardware/camera2/params/RecommendedStreamConfigurationMap.java
@@ -16,6 +16,8 @@
package android.hardware.camera2.params;
+import static com.android.internal.R.string.hardware;
+
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
@@ -149,6 +151,16 @@ public final class RecommendedStreamConfigurationMap {
public static final int USECASE_LOW_LATENCY_SNAPSHOT = 0x6;
/**
+ * If supported, the recommended 10-bit output stream configurations must include
+ * a subset of the advertised {@link android.graphics.ImageFormat#YCBCR_P010} and
+ * {@link android.graphics.ImageFormat#PRIVATE} outputs that are optimized for power
+ * and performance when registered along with a supported 10-bit dynamic range profile.
+ * {@see android.hardware.camera2.params.OutputConfiguration#setDynamicRangeProfile} for
+ * details.
+ */
+ public static final int USECASE_10BIT_OUTPUT = 0x8;
+
+ /**
* Device specific use cases.
* @hide
*/
@@ -163,7 +175,8 @@ public final class RecommendedStreamConfigurationMap {
USECASE_SNAPSHOT,
USECASE_ZSL,
USECASE_RAW,
- USECASE_LOW_LATENCY_SNAPSHOT})
+ USECASE_LOW_LATENCY_SNAPSHOT,
+ USECASE_10BIT_OUTPUT})
public @interface RecommendedUsecase {};
/**
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 89ac8bf2d7bc..eefa1d3279e3 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -334,6 +334,7 @@ public final class DisplayManager {
* @hide
*/
@TestApi
+ @SystemApi
public static final int VIRTUAL_DISPLAY_FLAG_TRUSTED = 1 << 10;
/**
diff --git a/core/java/android/hardware/face/FaceAuthenticationFrame.java b/core/java/android/hardware/face/FaceAuthenticationFrame.java
index f39d63411825..a53aad74d4e0 100644
--- a/core/java/android/hardware/face/FaceAuthenticationFrame.java
+++ b/core/java/android/hardware/face/FaceAuthenticationFrame.java
@@ -46,7 +46,7 @@ public final class FaceAuthenticationFrame implements Parcelable {
}
private FaceAuthenticationFrame(@NonNull Parcel source) {
- mData = source.readParcelable(FaceDataFrame.class.getClassLoader());
+ mData = source.readParcelable(FaceDataFrame.class.getClassLoader(), android.hardware.face.FaceDataFrame.class);
}
@Override
diff --git a/core/java/android/hardware/face/FaceEnrollFrame.java b/core/java/android/hardware/face/FaceEnrollFrame.java
index 822a57944449..bbccee2e2c3d 100644
--- a/core/java/android/hardware/face/FaceEnrollFrame.java
+++ b/core/java/android/hardware/face/FaceEnrollFrame.java
@@ -73,9 +73,9 @@ public final class FaceEnrollFrame implements Parcelable {
}
private FaceEnrollFrame(@NonNull Parcel source) {
- mCell = source.readParcelable(FaceEnrollCell.class.getClassLoader());
+ mCell = source.readParcelable(FaceEnrollCell.class.getClassLoader(), android.hardware.face.FaceEnrollCell.class);
mStage = source.readInt();
- mData = source.readParcelable(FaceDataFrame.class.getClassLoader());
+ mData = source.readParcelable(FaceDataFrame.class.getClassLoader(), android.hardware.face.FaceDataFrame.class);
}
@Override
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index 2ac194b67192..0304815ef8fe 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -50,6 +50,10 @@ interface IInputManager {
// Reports whether the hardware supports the given keys; returns true if successful
boolean hasKeys(int deviceId, int sourceMask, in int[] keyCodes, out boolean[] keyExists);
+ // Returns the keyCode produced when pressing the key at the specified location, given the
+ // active keyboard layout.
+ int getKeyCodeForKeyLocation(int deviceId, in int locationKeyCode);
+
// Temporarily changes the pointer speed.
void tryPointerSpeed(int speed);
diff --git a/core/java/android/hardware/input/InputDeviceIdentifier.java b/core/java/android/hardware/input/InputDeviceIdentifier.java
index c673e7ab7c53..a5b9a2a4da76 100644
--- a/core/java/android/hardware/input/InputDeviceIdentifier.java
+++ b/core/java/android/hardware/input/InputDeviceIdentifier.java
@@ -16,7 +16,9 @@
package android.hardware.input;
+import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
@@ -28,12 +30,13 @@ import java.util.Objects;
*
* @hide
*/
+@TestApi
public final class InputDeviceIdentifier implements Parcelable {
private final String mDescriptor;
private final int mVendorId;
private final int mProductId;
- public InputDeviceIdentifier(String descriptor, int vendorId, int productId) {
+ public InputDeviceIdentifier(@NonNull String descriptor, int vendorId, int productId) {
this.mDescriptor = descriptor;
this.mVendorId = vendorId;
this.mProductId = productId;
@@ -51,12 +54,13 @@ public final class InputDeviceIdentifier implements Parcelable {
}
@Override
- public void writeToParcel(Parcel dest, int flags) {
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeString(mDescriptor);
dest.writeInt(mVendorId);
dest.writeInt(mProductId);
}
+ @NonNull
public String getDescriptor() {
return mDescriptor;
}
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index ef349a96ee17..cbc837393b6b 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -58,6 +58,7 @@ import android.util.SparseArray;
import android.view.InputDevice;
import android.view.InputEvent;
import android.view.InputMonitor;
+import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.PointerIcon;
import android.view.VerifiedInputEvent;
@@ -637,6 +638,30 @@ public final class InputManager {
}
/**
+ * Returns the descriptors of all supported keyboard layouts appropriate for the specified
+ * input device.
+ * <p>
+ * The input manager consults the built-in keyboard layouts as well as all keyboard layouts
+ * advertised by applications using a {@link #ACTION_QUERY_KEYBOARD_LAYOUTS} broadcast receiver.
+ * </p>
+ *
+ * @param device The input device to query.
+ * @return The ids of all keyboard layouts which are supported by the specified input device.
+ *
+ * @hide
+ */
+ @TestApi
+ @NonNull
+ public List<String> getKeyboardLayoutDescriptorsForInputDevice(@NonNull InputDevice device) {
+ KeyboardLayout[] layouts = getKeyboardLayoutsForInputDevice(device.getIdentifier());
+ List<String> res = new ArrayList<>();
+ for (KeyboardLayout kl : layouts) {
+ res.add(kl.getDescriptor());
+ }
+ return res;
+ }
+
+ /**
* Gets information about all supported keyboard layouts appropriate
* for a specific input device.
* <p>
@@ -650,7 +675,9 @@ public final class InputManager {
*
* @hide
*/
- public KeyboardLayout[] getKeyboardLayoutsForInputDevice(InputDeviceIdentifier identifier) {
+ @NonNull
+ public KeyboardLayout[] getKeyboardLayoutsForInputDevice(
+ @NonNull InputDeviceIdentifier identifier) {
try {
return mIm.getKeyboardLayoutsForInputDevice(identifier);
} catch (RemoteException ex) {
@@ -680,15 +707,17 @@ public final class InputManager {
}
/**
- * Gets the current keyboard layout descriptor for the specified input
- * device.
+ * Gets the current keyboard layout descriptor for the specified input device.
*
* @param identifier Identifier for the input device
- * @return The keyboard layout descriptor, or null if no keyboard layout has
- * been set.
+ * @return The keyboard layout descriptor, or null if no keyboard layout has been set.
+ *
* @hide
*/
- public String getCurrentKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier) {
+ @TestApi
+ @Nullable
+ public String getCurrentKeyboardLayoutForInputDevice(
+ @NonNull InputDeviceIdentifier identifier) {
try {
return mIm.getCurrentKeyboardLayoutForInputDevice(identifier);
} catch (RemoteException ex) {
@@ -697,20 +726,21 @@ public final class InputManager {
}
/**
- * Sets the current keyboard layout descriptor for the specified input
- * device.
+ * Sets the current keyboard layout descriptor for the specified input device.
* <p>
- * This method may have the side-effect of causing the input device in
- * question to be reconfigured.
+ * This method may have the side-effect of causing the input device in question to be
+ * reconfigured.
* </p>
*
* @param identifier The identifier for the input device.
- * @param keyboardLayoutDescriptor The keyboard layout descriptor to use,
- * must not be null.
+ * @param keyboardLayoutDescriptor The keyboard layout descriptor to use, must not be null.
+ *
* @hide
*/
- public void setCurrentKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier,
- String keyboardLayoutDescriptor) {
+ @TestApi
+ @RequiresPermission(Manifest.permission.SET_KEYBOARD_LAYOUT)
+ public void setCurrentKeyboardLayoutForInputDevice(@NonNull InputDeviceIdentifier identifier,
+ @NonNull String keyboardLayoutDescriptor) {
if (identifier == null) {
throw new IllegalArgumentException("identifier must not be null");
}
@@ -727,11 +757,11 @@ public final class InputManager {
}
/**
- * Gets all keyboard layout descriptors that are enabled for the specified
- * input device.
+ * Gets all keyboard layout descriptors that are enabled for the specified input device.
*
* @param identifier The identifier for the input device.
* @return The keyboard layout descriptors.
+ *
* @hide
*/
public String[] getEnabledKeyboardLayoutsForInputDevice(InputDeviceIdentifier identifier) {
@@ -749,15 +779,16 @@ public final class InputManager {
/**
* Adds the keyboard layout descriptor for the specified input device.
* <p>
- * This method may have the side-effect of causing the input device in
- * question to be reconfigured.
+ * This method may have the side-effect of causing the input device in question to be
+ * reconfigured.
* </p>
*
* @param identifier The identifier for the input device.
- * @param keyboardLayoutDescriptor The descriptor of the keyboard layout to
- * add.
+ * @param keyboardLayoutDescriptor The descriptor of the keyboard layout to add.
+ *
* @hide
*/
+ @RequiresPermission(Manifest.permission.SET_KEYBOARD_LAYOUT)
public void addKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier,
String keyboardLayoutDescriptor) {
if (identifier == null) {
@@ -777,17 +808,19 @@ public final class InputManager {
/**
* Removes the keyboard layout descriptor for the specified input device.
* <p>
- * This method may have the side-effect of causing the input device in
- * question to be reconfigured.
+ * This method may have the side-effect of causing the input device in question to be
+ * reconfigured.
* </p>
*
* @param identifier The identifier for the input device.
- * @param keyboardLayoutDescriptor The descriptor of the keyboard layout to
- * remove.
+ * @param keyboardLayoutDescriptor The descriptor of the keyboard layout to remove.
+ *
* @hide
*/
- public void removeKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier,
- String keyboardLayoutDescriptor) {
+ @TestApi
+ @RequiresPermission(Manifest.permission.SET_KEYBOARD_LAYOUT)
+ public void removeKeyboardLayoutForInputDevice(@NonNull InputDeviceIdentifier identifier,
+ @NonNull String keyboardLayoutDescriptor) {
if (identifier == null) {
throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
}
@@ -1044,6 +1077,27 @@ public final class InputManager {
return ret;
}
+ /**
+ * Gets the key code produced by the specified location on a US keyboard layout.
+ * Key code as defined in {@link android.view.KeyEvent}.
+ * This API is only functional for devices with {@link InputDevice#SOURCE_KEYBOARD} available
+ * which can alter their key mapping using country specific keyboard layouts.
+ *
+ * @param deviceId The input device id.
+ * @param locationKeyCode The location of a key on a US keyboard layout.
+ * @return The key code produced when pressing the key at the specified location, given the
+ * active keyboard layout. Returns {@link KeyEvent#KEYCODE_UNKNOWN} if the requested
+ * mapping could not be determined, or if an error occurred.
+ * @hide
+ */
+ public int getKeyCodeForKeyLocation(int deviceId, int locationKeyCode) {
+ try {
+ return mIm.getKeyCodeForKeyLocation(deviceId, locationKeyCode);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
/**
* Injects an input event into the event system on behalf of an application.
diff --git a/core/java/android/hardware/input/InputManagerInternal.java b/core/java/android/hardware/input/InputManagerInternal.java
index 1173c311bd26..f866a2e1c691 100644
--- a/core/java/android/hardware/input/InputManagerInternal.java
+++ b/core/java/android/hardware/input/InputManagerInternal.java
@@ -17,6 +17,7 @@
package android.hardware.input;
import android.annotation.NonNull;
+import android.graphics.PointF;
import android.hardware.display.DisplayViewport;
import android.os.IBinder;
import android.view.InputEvent;
@@ -79,6 +80,22 @@ public abstract class InputManagerInternal {
public abstract boolean transferTouchFocus(@NonNull IBinder fromChannelToken,
@NonNull IBinder toChannelToken);
+ /**
+ * Sets the display id that the MouseCursorController will be forced to target. Pass
+ * {@link android.view.Display#INVALID_DISPLAY} to clear the override.
+ */
+ public abstract void setVirtualMousePointerDisplayId(int pointerDisplayId);
+
+ /** Gets the current position of the mouse cursor. */
+ public abstract PointF getCursorPosition();
+
+ /**
+ * Sets the eligibility of windows on a given display for pointer capture. If a display is
+ * marked ineligible, requests to enable pointer capture for windows on that display will be
+ * ignored.
+ */
+ public abstract void setDisplayEligibilityForPointerCapture(int displayId, boolean isEligible);
+
/** Registers the {@link LidSwitchCallback} to begin receiving notifications. */
public abstract void registerLidSwitchCallback(@NonNull LidSwitchCallback callbacks);
diff --git a/core/java/android/hardware/input/VirtualMouse.java b/core/java/android/hardware/input/VirtualMouse.java
index 6599dd2e28eb..6e2b56a2b5bc 100644
--- a/core/java/android/hardware/input/VirtualMouse.java
+++ b/core/java/android/hardware/input/VirtualMouse.java
@@ -20,6 +20,7 @@ import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.companion.virtual.IVirtualDevice;
+import android.graphics.PointF;
import android.os.IBinder;
import android.os.RemoteException;
import android.view.MotionEvent;
@@ -61,6 +62,8 @@ public class VirtualMouse implements Closeable {
* Send a mouse button event to the system.
*
* @param event the event
+ * @throws IllegalStateException if the display this mouse is associated with is not currently
+ * targeted
*/
@RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
public void sendButtonEvent(@NonNull VirtualMouseButtonEvent event) {
@@ -76,6 +79,8 @@ public class VirtualMouse implements Closeable {
* {@link MotionEvent#AXIS_SCROLL}.
*
* @param event the event
+ * @throws IllegalStateException if the display this mouse is associated with is not currently
+ * targeted
*/
@RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
public void sendScrollEvent(@NonNull VirtualMouseScrollEvent event) {
@@ -90,6 +95,8 @@ public class VirtualMouse implements Closeable {
* Sends a relative movement event to the system.
*
* @param event the event
+ * @throws IllegalStateException if the display this mouse is associated with is not currently
+ * targeted
*/
@RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
public void sendRelativeEvent(@NonNull VirtualMouseRelativeEvent event) {
@@ -99,4 +106,20 @@ public class VirtualMouse implements Closeable {
throw e.rethrowFromSystemServer();
}
}
+
+ /**
+ * Gets the current cursor position.
+ *
+ * @return the position, expressed as x and y coordinates
+ * @throws IllegalStateException if the display this mouse is associated with is not currently
+ * targeted
+ */
+ @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+ public @NonNull PointF getCursorPosition() {
+ try {
+ return mVirtualDevice.getCursorPosition(mToken);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/hardware/location/GeofenceHardwareMonitorEvent.java b/core/java/android/hardware/location/GeofenceHardwareMonitorEvent.java
index 78cca9601a2d..310ebe9ac093 100644
--- a/core/java/android/hardware/location/GeofenceHardwareMonitorEvent.java
+++ b/core/java/android/hardware/location/GeofenceHardwareMonitorEvent.java
@@ -81,7 +81,7 @@ public class GeofenceHardwareMonitorEvent implements Parcelable {
int monitoringType = source.readInt();
int monitoringStatus = source.readInt();
int sourceTechnologies = source.readInt();
- Location location = source.readParcelable(classLoader);
+ Location location = source.readParcelable(classLoader, android.location.Location.class);
return new GeofenceHardwareMonitorEvent(
monitoringType,
diff --git a/core/java/android/hardware/usb/IUsbManager.aidl b/core/java/android/hardware/usb/IUsbManager.aidl
index 7f07af79ab69..459dab1bffe1 100644
--- a/core/java/android/hardware/usb/IUsbManager.aidl
+++ b/core/java/android/hardware/usb/IUsbManager.aidl
@@ -18,6 +18,7 @@ package android.hardware.usb;
import android.app.PendingIntent;
import android.content.ComponentName;
+import android.hardware.usb.IUsbOperationInternal;
import android.hardware.usb.UsbAccessory;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.ParcelableUsbPort;
@@ -136,7 +137,10 @@ interface IUsbManager
void resetUsbGadget();
/* Set USB data on or off */
- boolean enableUsbDataSignal(boolean enable);
+ boolean enableUsbData(in String portId, boolean enable, int operationId, in IUsbOperationInternal callback);
+
+ /* Enable USB data when disabled due to docking event */
+ void enableUsbDataWhileDocked(in String portId, int operationId, in IUsbOperationInternal callback);
/* Gets the USB Hal Version. */
int getUsbHalVersion();
@@ -156,9 +160,14 @@ interface IUsbManager
/* Sets the port's current role. */
void setPortRoles(in String portId, int powerRole, int dataRole);
+ /* Limit power transfer in & out of the port within the allowed limit by the USB
+ * specification.
+ */
+ void enableLimitPowerTransfer(in String portId, boolean limit, int operationId, in IUsbOperationInternal callback);
+
/* Enable/disable contaminant detection */
void enableContaminantDetection(in String portId, boolean enable);
- /* Sets USB device connection handler. */
- void setUsbDeviceConnectionHandler(in ComponentName usbDeviceConnectionHandler);
+ /* Sets USB device connection handler. */
+ void setUsbDeviceConnectionHandler(in ComponentName usbDeviceConnectionHandler);
}
diff --git a/core/java/android/hardware/usb/IUsbOperationInternal.aidl b/core/java/android/hardware/usb/IUsbOperationInternal.aidl
new file mode 100644
index 000000000000..3f3bbf63ed8b
--- /dev/null
+++ b/core/java/android/hardware/usb/IUsbOperationInternal.aidl
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.usb;
+
+/**
+ * @hide
+ */
+oneway interface IUsbOperationInternal {
+void onOperationComplete(in int status);
+}
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index c29a948c2a26..f0e040ed4686 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -36,6 +36,8 @@ import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.hardware.usb.gadget.V1_0.GadgetFunction;
import android.hardware.usb.gadget.V1_2.UsbSpeed;
+import android.hardware.usb.IUsbOperationInternal;
+import android.hardware.usb.UsbPort;
import android.os.Build;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
@@ -48,6 +50,7 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.StringJoiner;
/**
@@ -517,6 +520,14 @@ public class UsbManager {
public static final int USB_DATA_TRANSFER_RATE_40G = 40 * 1024;
/**
+ * Returned when the client has to retry querying the version.
+ *
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public static final int USB_HAL_RETRY = -2;
+
+ /**
* The Value for USB hal is not presented.
*
* {@hide}
@@ -557,6 +568,14 @@ public class UsbManager {
public static final int USB_HAL_V1_3 = 13;
/**
+ * Value for USB Hal Version v2.0.
+ *
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public static final int USB_HAL_V2_0 = 20;
+
+ /**
* Code for the charging usb function. Passed into {@link #setCurrentFunctions(long)}
* {@hide}
*/
@@ -665,6 +684,7 @@ public class UsbManager {
USB_HAL_V1_1,
USB_HAL_V1_2,
USB_HAL_V1_3,
+ USB_HAL_V2_0,
})
public @interface UsbHalVersion {}
@@ -1169,8 +1189,9 @@ public class UsbManager {
/**
* Enable/Disable the USB data signaling.
* <p>
- * Enables/Disables USB data path in all the USB ports.
+ * Enables/Disables USB data path of the first port..
* It will force to stop or restore USB data signaling.
+ * Call UsbPort API if the device has more than one UsbPort.
* </p>
*
* @param enable enable or disable USB data signaling
@@ -1181,11 +1202,11 @@ public class UsbManager {
*/
@RequiresPermission(Manifest.permission.MANAGE_USB)
public boolean enableUsbDataSignal(boolean enable) {
- try {
- return mService.enableUsbDataSignal(enable);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ List<UsbPort> usbPorts = getPorts();
+ if (usbPorts.size() == 1) {
+ return usbPorts.get(0).enableUsbData(enable) == UsbPort.ENABLE_USB_DATA_SUCCESS;
}
+ return false;
}
/**
@@ -1271,6 +1292,102 @@ public class UsbManager {
}
/**
+ * Should only be called by {@link UsbPort#enableLimitPowerTransfer}.
+ * <p>
+ * limits or restores power transfer in and out of USB port.
+ *
+ * @param port USB port for which power transfer has to be limited or restored.
+ * @param limit limit power transfer when true.
+ * relax power transfer restrictions when false.
+ * @param operationId operationId for the request.
+ * @param callback callback object to be invoked when the operation is complete.
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.MANAGE_USB)
+ void enableLimitPowerTransfer(@NonNull UsbPort port, boolean limit, int operationId,
+ IUsbOperationInternal callback) {
+ Objects.requireNonNull(port, "enableLimitPowerTransfer:port must not be null. opId:"
+ + operationId);
+ try {
+ mService.enableLimitPowerTransfer(port.getId(), limit, operationId, callback);
+ } catch (RemoteException e) {
+ Log.e(TAG, "enableLimitPowerTransfer failed. opId:" + operationId, e);
+ try {
+ callback.onOperationComplete(UsbOperationInternal.USB_OPERATION_ERROR_INTERNAL);
+ } catch (RemoteException r) {
+ Log.e(TAG, "enableLimitPowerTransfer failed to call onOperationComplete. opId:"
+ + operationId, r);
+ }
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Should only be called by {@link UsbPort#enableUsbData}.
+ * <p>
+ * Enables or disables USB data on the specific port.
+ *
+ * @param port USB port for which USB data needs to be enabled or disabled.
+ * @param enable Enable USB data when true.
+ * Disable USB data when false.
+ * @param operationId operationId for the request.
+ * @param callback callback object to be invoked when the operation is complete.
+ * @return True when the operation is asynchronous. The caller must therefore call
+ * {@link UsbOperationInternal#waitForOperationComplete} for processing
+ * the result.
+ * False when the operation is synchronous. Caller can proceed reading the result
+ * through {@link UsbOperationInternal#getStatus}
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.MANAGE_USB)
+ boolean enableUsbData(@NonNull UsbPort port, boolean enable, int operationId,
+ IUsbOperationInternal callback) {
+ Objects.requireNonNull(port, "enableUsbData: port must not be null. opId:" + operationId);
+ try {
+ return mService.enableUsbData(port.getId(), enable, operationId, callback);
+ } catch (RemoteException e) {
+ Log.e(TAG, "enableUsbData: failed. opId:" + operationId, e);
+ try {
+ callback.onOperationComplete(UsbOperationInternal.USB_OPERATION_ERROR_INTERNAL);
+ } catch (RemoteException r) {
+ Log.e(TAG, "enableUsbData: failed to call onOperationComplete. opId:"
+ + operationId, r);
+ }
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Should only be called by {@link UsbPort#enableUsbDataWhileDocked}.
+ * <p>
+ * Enables or disables USB data when disabled due to docking event.
+ *
+ * @param port USB port for which USB data needs to be enabled.
+ * @param operationId operationId for the request.
+ * @param callback callback object to be invoked when the operation is complete.
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.MANAGE_USB)
+ void enableUsbDataWhileDocked(@NonNull UsbPort port, int operationId,
+ IUsbOperationInternal callback) {
+ Objects.requireNonNull(port, "enableUsbDataWhileDocked: port must not be null. opId:"
+ + operationId);
+ try {
+ mService.enableUsbDataWhileDocked(port.getId(), operationId, callback);
+ } catch (RemoteException e) {
+ Log.e(TAG, "enableUsbDataWhileDocked: failed. opId:" + operationId, e);
+ try {
+ callback.onOperationComplete(UsbOperationInternal.USB_OPERATION_ERROR_INTERNAL);
+ } catch (RemoteException r) {
+ Log.e(TAG, "enableUsbDataWhileDocked: failed to call onOperationComplete. opId:"
+ + operationId, r);
+ }
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Sets the component that will handle USB device connection.
* <p>
* Setting component allows to specify external USB host manager to handle use cases, where
diff --git a/core/java/android/hardware/usb/UsbOperationInternal.java b/core/java/android/hardware/usb/UsbOperationInternal.java
new file mode 100644
index 000000000000..9bc2b3892a1e
--- /dev/null
+++ b/core/java/android/hardware/usb/UsbOperationInternal.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.usb;
+
+import android.annotation.IntDef;
+import android.hardware.usb.IUsbOperationInternal;
+import android.hardware.usb.UsbPort;
+import android.util.Log;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.concurrent.TimeUnit;
+/**
+ * UsbOperationInternal allows UsbPort to support both synchronous and
+ * asynchronous function irrespective of whether the underlying hal
+ * method is synchronous or asynchronous.
+ *
+ * @hide
+ */
+public final class UsbOperationInternal extends IUsbOperationInternal.Stub {
+ private static final String TAG = "UsbPortStatus";
+ private final int mOperationID;
+ // Cached portId.
+ private final String mId;
+ // True implies operation did not timeout.
+ private boolean mOperationComplete;
+ private @UsbOperationStatus int mStatus;
+ final ReentrantLock mLock = new ReentrantLock();
+ final Condition mOperationWait = mLock.newCondition();
+ // Maximum time the caller has to wait for onOperationComplete to be called.
+ private static final int USB_OPERATION_TIMEOUT_MSECS = 5000;
+
+ /**
+ * The requested operation was successfully completed.
+ * Returned in {@link onOperationComplete} and {@link getStatus}.
+ */
+ public static final int USB_OPERATION_SUCCESS = 0;
+
+ /**
+ * The requested operation failed due to internal error.
+ * Returned in {@link onOperationComplete} and {@link getStatus}.
+ */
+ public static final int USB_OPERATION_ERROR_INTERNAL = 1;
+
+ /**
+ * The requested operation failed as it's not supported.
+ * Returned in {@link onOperationComplete} and {@link getStatus}.
+ */
+ public static final int USB_OPERATION_ERROR_NOT_SUPPORTED = 2;
+
+ /**
+ * The requested operation failed as it's not supported.
+ * Returned in {@link onOperationComplete} and {@link getStatus}.
+ */
+ public static final int USB_OPERATION_ERROR_PORT_MISMATCH = 3;
+
+ @IntDef(prefix = { "USB_OPERATION_" }, value = {
+ USB_OPERATION_SUCCESS,
+ USB_OPERATION_ERROR_INTERNAL,
+ USB_OPERATION_ERROR_NOT_SUPPORTED,
+ USB_OPERATION_ERROR_PORT_MISMATCH
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface UsbOperationStatus{}
+
+ UsbOperationInternal(int operationID, String id) {
+ this.mOperationID = operationID;
+ this.mId = id;
+ }
+
+ /**
+ * Hal glue layer would directly call this function when the requested
+ * operation is complete.
+ */
+ @Override
+ public void onOperationComplete(@UsbOperationStatus int status) {
+ mLock.lock();
+ try {
+ mOperationComplete = true;
+ mStatus = status;
+ Log.i(TAG, "Port:" + mId + " opID:" + mOperationID + " status:" + mStatus);
+ mOperationWait.signal();
+ } finally {
+ mLock.unlock();
+ }
+ }
+
+ /**
+ * Caller invokes this function to wait for the operation to be complete.
+ */
+ public void waitForOperationComplete() {
+ mLock.lock();
+ try {
+ long now = System.currentTimeMillis();
+ long deadline = now + USB_OPERATION_TIMEOUT_MSECS;
+ // Wait in loop to overcome spurious wakeups.
+ do {
+ mOperationWait.await(deadline - System.currentTimeMillis(),
+ TimeUnit.MILLISECONDS);
+ } while (!mOperationComplete && System.currentTimeMillis() < deadline);
+ if (!mOperationComplete) {
+ Log.e(TAG, "Port:" + mId + " opID:" + mOperationID
+ + " operationComplete not received in " + USB_OPERATION_TIMEOUT_MSECS
+ + "msecs");
+ }
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Port:" + mId + " opID:" + mOperationID + " operationComplete interrupted");
+ } finally {
+ mLock.unlock();
+ }
+ }
+
+ public @UsbOperationStatus int getStatus() {
+ return mOperationComplete ? mStatus : USB_OPERATION_ERROR_INTERNAL;
+ }
+}
diff --git a/core/java/android/hardware/usb/UsbPort.java b/core/java/android/hardware/usb/UsbPort.java
index 274e23fff292..bef4dea019a2 100644
--- a/core/java/android/hardware/usb/UsbPort.java
+++ b/core/java/android/hardware/usb/UsbPort.java
@@ -16,6 +16,10 @@
package android.hardware.usb;
+import static android.hardware.usb.UsbOperationInternal.USB_OPERATION_ERROR_INTERNAL;
+import static android.hardware.usb.UsbOperationInternal.USB_OPERATION_ERROR_NOT_SUPPORTED;
+import static android.hardware.usb.UsbOperationInternal.USB_OPERATION_ERROR_PORT_MISMATCH;
+import static android.hardware.usb.UsbOperationInternal.USB_OPERATION_SUCCESS;
import static android.hardware.usb.UsbPortStatus.CONTAMINANT_DETECTION_DETECTED;
import static android.hardware.usb.UsbPortStatus.CONTAMINANT_DETECTION_DISABLED;
import static android.hardware.usb.UsbPortStatus.CONTAMINANT_DETECTION_NOT_DETECTED;
@@ -29,20 +33,38 @@ import static android.hardware.usb.UsbPortStatus.MODE_DFP;
import static android.hardware.usb.UsbPortStatus.MODE_DUAL;
import static android.hardware.usb.UsbPortStatus.MODE_NONE;
import static android.hardware.usb.UsbPortStatus.MODE_UFP;
+import static android.hardware.usb.UsbPortStatus.POWER_BRICK_STATUS_DISCONNECTED;
+import static android.hardware.usb.UsbPortStatus.POWER_BRICK_STATUS_UNKNOWN;
+import static android.hardware.usb.UsbPortStatus.POWER_BRICK_STATUS_CONNECTED;
import static android.hardware.usb.UsbPortStatus.POWER_ROLE_NONE;
import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SINK;
import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SOURCE;
+import static android.hardware.usb.UsbPortStatus.USB_DATA_STATUS_UNKNOWN;
+import static android.hardware.usb.UsbPortStatus.USB_DATA_STATUS_ENABLED;
+import static android.hardware.usb.UsbPortStatus.USB_DATA_STATUS_DISABLED_OVERHEAT;
+import static android.hardware.usb.UsbPortStatus.USB_DATA_STATUS_DISABLED_CONTAMINANT;
+import static android.hardware.usb.UsbPortStatus.USB_DATA_STATUS_DISABLED_DOCK;
+import static android.hardware.usb.UsbPortStatus.USB_DATA_STATUS_DISABLED_FORCE;
+import static android.hardware.usb.UsbPortStatus.USB_DATA_STATUS_DISABLED_DEBUG;
import android.Manifest;
+import android.annotation.CheckResult;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
+import android.hardware.usb.UsbOperationInternal;
import android.hardware.usb.V1_0.Constants;
+import android.os.Binder;
+import android.util.Log;
import com.android.internal.util.Preconditions;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
+import java.util.concurrent.atomic.AtomicInteger;
/**
* Represents a physical USB port and describes its characteristics.
@@ -51,6 +73,7 @@ import java.util.Objects;
*/
@SystemApi
public final class UsbPort {
+ private static final String TAG = "UsbPort";
private final String mId;
private final int mSupportedModes;
private final UsbManager mUsbManager;
@@ -64,6 +87,125 @@ public final class UsbPort {
*/
private static final int POWER_ROLE_OFFSET = Constants.PortPowerRole.NONE;
+ /**
+ * Counter for tracking UsbOperation operations.
+ */
+ private static final AtomicInteger sUsbOperationCount = new AtomicInteger();
+
+ /**
+ * The {@link #enableUsbData} request was successfully completed.
+ */
+ public static final int ENABLE_USB_DATA_SUCCESS = 0;
+
+ /**
+ * The {@link #enableUsbData} request failed due to internal error.
+ */
+ public static final int ENABLE_USB_DATA_ERROR_INTERNAL = 1;
+
+ /**
+ * The {@link #enableUsbData} request failed as it's not supported.
+ */
+ public static final int ENABLE_USB_DATA_ERROR_NOT_SUPPORTED = 2;
+
+ /**
+ * The {@link #enableUsbData} request failed as port id mismatched.
+ */
+ public static final int ENABLE_USB_DATA_ERROR_PORT_MISMATCH = 3;
+
+ /**
+ * The {@link #enableUsbData} request failed due to other reasons.
+ */
+ public static final int ENABLE_USB_DATA_ERROR_OTHER = 4;
+
+ /** @hide */
+ @IntDef(prefix = { "ENABLE_USB_DATA_" }, value = {
+ ENABLE_USB_DATA_SUCCESS,
+ ENABLE_USB_DATA_ERROR_INTERNAL,
+ ENABLE_USB_DATA_ERROR_NOT_SUPPORTED,
+ ENABLE_USB_DATA_ERROR_PORT_MISMATCH,
+ ENABLE_USB_DATA_ERROR_OTHER
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface EnableUsbDataStatus{}
+
+ /**
+ * The {@link #enableLimitPowerTransfer} request was successfully completed.
+ */
+ public static final int ENABLE_LIMIT_POWER_TRANSFER_SUCCESS = 0;
+
+ /**
+ * The {@link #enableLimitPowerTransfer} request failed due to internal error.
+ */
+ public static final int ENABLE_LIMIT_POWER_TRANSFER_ERROR_INTERNAL = 1;
+
+ /**
+ * The {@link #enableLimitPowerTransfer} request failed as it's not supported.
+ */
+ public static final int ENABLE_LIMIT_POWER_TRANSFER_ERROR_NOT_SUPPORTED = 2;
+
+ /**
+ * The {@link #enableLimitPowerTransfer} request failed as port id mismatched.
+ */
+ public static final int ENABLE_LIMIT_POWER_TRANSFER_ERROR_PORT_MISMATCH = 3;
+
+ /**
+ * The {@link #enableLimitPowerTransfer} request failed due to other reasons.
+ */
+ public static final int ENABLE_LIMIT_POWER_TRANSFER_ERROR_OTHER = 4;
+
+ /** @hide */
+ @IntDef(prefix = { "ENABLE_LIMIT_POWER_TRANSFER_" }, value = {
+ ENABLE_LIMIT_POWER_TRANSFER_SUCCESS,
+ ENABLE_LIMIT_POWER_TRANSFER_ERROR_INTERNAL,
+ ENABLE_LIMIT_POWER_TRANSFER_ERROR_NOT_SUPPORTED,
+ ENABLE_LIMIT_POWER_TRANSFER_ERROR_PORT_MISMATCH,
+ ENABLE_LIMIT_POWER_TRANSFER_ERROR_OTHER
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface EnableLimitPowerTransferStatus{}
+
+ /**
+ * The {@link #enableUsbDataWhileDocked} request was successfully completed.
+ */
+ public static final int ENABLE_USB_DATA_WHILE_DOCKED_SUCCESS = 0;
+
+ /**
+ * The {@link #enableUsbDataWhileDocked} request failed due to internal error.
+ */
+ public static final int ENABLE_USB_DATA_WHILE_DOCKED_ERROR_INTERNAL = 1;
+
+ /**
+ * The {@link #enableUsbDataWhileDocked} request failed as it's not supported.
+ */
+ public static final int ENABLE_USB_DATA_WHILE_DOCKED_ERROR_NOT_SUPPORTED = 2;
+
+ /**
+ * The {@link #enableUsbDataWhileDocked} request failed as port id mismatched.
+ */
+ public static final int ENABLE_USB_DATA_WHILE_DOCKED_ERROR_PORT_MISMATCH = 3;
+
+ /**
+ * The {@link #enableUsbDataWhileDocked} request failed as data is still enabled.
+ */
+ public static final int ENABLE_USB_DATA_WHILE_DOCKED_ERROR_DATA_ENABLED = 4;
+
+ /**
+ * The {@link #enableUsbDataWhileDocked} request failed due to other reasons.
+ */
+ public static final int ENABLE_USB_DATA_WHILE_DOCKED_ERROR_OTHER = 5;
+
+ /** @hide */
+ @IntDef(prefix = { "ENABLE_USB_DATA_WHILE_DOCKED_" }, value = {
+ ENABLE_USB_DATA_WHILE_DOCKED_SUCCESS,
+ ENABLE_USB_DATA_WHILE_DOCKED_ERROR_INTERNAL,
+ ENABLE_USB_DATA_WHILE_DOCKED_ERROR_NOT_SUPPORTED,
+ ENABLE_USB_DATA_WHILE_DOCKED_ERROR_PORT_MISMATCH,
+ ENABLE_USB_DATA_WHILE_DOCKED_ERROR_DATA_ENABLED,
+ ENABLE_USB_DATA_WHILE_DOCKED_ERROR_OTHER
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface EnableUsbDataWhileDockedStatus{}
+
/** @hide */
public UsbPort(@NonNull UsbManager usbManager, @NonNull String id, int supportedModes,
int supportedContaminantProtectionModes,
@@ -157,7 +299,7 @@ public final class UsbPort {
* {@link UsbPortStatus#isRoleCombinationSupported UsbPortStatus.isRoleCombinationSupported}.
* </p><p>
* Note: This function is asynchronous and may fail silently without applying
- * the requested changes. If this function does cause a status change to occur then
+ * the operationed changes. If this function does cause a status change to occur then
* a {@link UsbManager#ACTION_USB_PORT_CHANGED} broadcast will be sent.
* </p>
*
@@ -177,6 +319,133 @@ public final class UsbPort {
}
/**
+ * Enables/Disables Usb data on the port.
+ *
+ * @param enable When true enables USB data if disabled.
+ * When false disables USB data if enabled.
+ * @return {@link #ENABLE_USB_DATA_SUCCESS} when request completes successfully or
+ * {@link #ENABLE_USB_DATA_ERROR_INTERNAL} when request fails due to internal
+ * error or
+ * {@link ENABLE_USB_DATA_ERROR_NOT_SUPPORTED} when not supported or
+ * {@link ENABLE_USB_DATA_ERROR_PORT_MISMATCH} when request fails due to port id
+ * mismatch or
+ * {@link ENABLE_USB_DATA_ERROR_OTHER} when fails due to other reasons.
+ */
+ @CheckResult
+ @RequiresPermission(Manifest.permission.MANAGE_USB)
+ public @EnableUsbDataStatus int enableUsbData(boolean enable) {
+ // UID is added To minimize operationID overlap between two different packages.
+ int operationId = sUsbOperationCount.incrementAndGet() + Binder.getCallingUid();
+ Log.i(TAG, "enableUsbData opId:" + operationId
+ + " callingUid:" + Binder.getCallingUid());
+ UsbOperationInternal opCallback =
+ new UsbOperationInternal(operationId, mId);
+ if (mUsbManager.enableUsbData(this, enable, operationId, opCallback) == true) {
+ opCallback.waitForOperationComplete();
+ }
+
+ int result = opCallback.getStatus();
+ switch (result) {
+ case USB_OPERATION_SUCCESS:
+ return ENABLE_USB_DATA_SUCCESS;
+ case USB_OPERATION_ERROR_INTERNAL:
+ return ENABLE_USB_DATA_ERROR_INTERNAL;
+ case USB_OPERATION_ERROR_NOT_SUPPORTED:
+ return ENABLE_USB_DATA_ERROR_NOT_SUPPORTED;
+ case USB_OPERATION_ERROR_PORT_MISMATCH:
+ return ENABLE_USB_DATA_ERROR_PORT_MISMATCH;
+ default:
+ return ENABLE_USB_DATA_ERROR_OTHER;
+ }
+ }
+
+ /**
+ * Enables Usb data when disabled due to {@link UsbPort#USB_DATA_STATUS_DISABLED_DOCK}
+ *
+ * @return {@link #ENABLE_USB_DATA_WHILE_DOCKED_SUCCESS} when request completes successfully or
+ * {@link #ENABLE_USB_DATA_WHILE_DOCKED_ERROR_INTERNAL} when request fails due to
+ * internal error or
+ * {@link ENABLE_USB_DATA_WHILE_DOCKED_ERROR_NOT_SUPPORTED} when not supported or
+ * {@link ENABLE_USB_DATA_WHILE_DOCKED_ERROR_PORT_MISMATCH} when request fails due to
+ * port id mismatch or
+ * {@link ENABLE_USB_DATA_WHILE_DOCKED_ERROR_DATA_ENABLED} when request fails as data
+ * is still enabled or
+ * {@link ENABLE_USB_DATA_WHILE_DOCKED_ERROR_OTHER} when fails due to other reasons.
+ */
+ @CheckResult
+ @RequiresPermission(Manifest.permission.MANAGE_USB)
+ public @EnableUsbDataWhileDockedStatus int enableUsbDataWhileDocked() {
+ // UID is added To minimize operationID overlap between two different packages.
+ int operationId = sUsbOperationCount.incrementAndGet() + Binder.getCallingUid();
+ Log.i(TAG, "enableUsbData opId:" + operationId
+ + " callingUid:" + Binder.getCallingUid());
+ UsbPortStatus portStatus = getStatus();
+ if (portStatus != null &&
+ !usbDataStatusToString(portStatus.getUsbDataStatus()).contains("disabled-dock")) {
+ return ENABLE_USB_DATA_WHILE_DOCKED_ERROR_DATA_ENABLED;
+ }
+
+ UsbOperationInternal opCallback =
+ new UsbOperationInternal(operationId, mId);
+ mUsbManager.enableUsbDataWhileDocked(this, operationId, opCallback);
+ opCallback.waitForOperationComplete();
+ int result = opCallback.getStatus();
+ switch (result) {
+ case USB_OPERATION_SUCCESS:
+ return ENABLE_USB_DATA_WHILE_DOCKED_SUCCESS;
+ case USB_OPERATION_ERROR_INTERNAL:
+ return ENABLE_USB_DATA_WHILE_DOCKED_ERROR_INTERNAL;
+ case USB_OPERATION_ERROR_NOT_SUPPORTED:
+ return ENABLE_USB_DATA_WHILE_DOCKED_ERROR_NOT_SUPPORTED;
+ case USB_OPERATION_ERROR_PORT_MISMATCH:
+ return ENABLE_USB_DATA_WHILE_DOCKED_ERROR_PORT_MISMATCH;
+ default:
+ return ENABLE_USB_DATA_WHILE_DOCKED_ERROR_OTHER;
+ }
+ }
+
+ /**
+ * Limits power transfer In and out of the port.
+ * <p>
+ * Disables charging and limits sourcing power(when permitted by the USB spec) until
+ * port disconnect event.
+ * </p>
+ * @param enable limits power transfer when true.
+ * @return {@link #ENABLE_LIMIT_POWER_TRANSFER_SUCCESS} when request completes successfully or
+ * {@link #ENABLE_LIMIT_POWER_TRANSFER_ERROR_INTERNAL} when request fails due to
+ * internal error or
+ * {@link ENABLE_LIMIT_POWER_TRANSFER_ERROR_NOT_SUPPORTED} when not supported or
+ * {@link ENABLE_LIMIT_POWER_TRANSFER_ERROR_PORT_MISMATCH} when request fails due to
+ * port id mismatch or
+ * {@link ENABLE_LIMIT_POWER_TRANSFER_ERROR_OTHER} when fails due to other reasons.
+ */
+ @CheckResult
+ @RequiresPermission(Manifest.permission.MANAGE_USB)
+ public @EnableLimitPowerTransferStatus int enableLimitPowerTransfer(boolean enable) {
+ // UID is added To minimize operationID overlap between two different packages.
+ int operationId = sUsbOperationCount.incrementAndGet() + Binder.getCallingUid();
+ Log.i(TAG, "enableLimitPowerTransfer opId:" + operationId
+ + " callingUid:" + Binder.getCallingUid());
+ UsbOperationInternal opCallback =
+ new UsbOperationInternal(operationId, mId);
+ mUsbManager.enableLimitPowerTransfer(this, enable, operationId, opCallback);
+ opCallback.waitForOperationComplete();
+ int result = opCallback.getStatus();
+ switch (result) {
+ case USB_OPERATION_SUCCESS:
+ return ENABLE_LIMIT_POWER_TRANSFER_SUCCESS;
+ case USB_OPERATION_ERROR_INTERNAL:
+ return ENABLE_LIMIT_POWER_TRANSFER_ERROR_INTERNAL;
+ case USB_OPERATION_ERROR_NOT_SUPPORTED:
+ return ENABLE_LIMIT_POWER_TRANSFER_ERROR_NOT_SUPPORTED;
+ case USB_OPERATION_ERROR_PORT_MISMATCH:
+ return ENABLE_LIMIT_POWER_TRANSFER_ERROR_PORT_MISMATCH;
+ default:
+ return ENABLE_LIMIT_POWER_TRANSFER_ERROR_OTHER;
+ }
+ }
+
+ /**
* @hide
**/
public void enableContaminantDetection(boolean enable) {
@@ -274,6 +543,57 @@ public final class UsbPort {
}
/** @hide */
+ public static String usbDataStatusToString(int usbDataStatus) {
+ switch (usbDataStatus) {
+ case USB_DATA_STATUS_UNKNOWN:
+ return "unknown";
+ case USB_DATA_STATUS_ENABLED:
+ return "enabled";
+ case USB_DATA_STATUS_DISABLED_OVERHEAT:
+ return "disabled-overheat";
+ case USB_DATA_STATUS_DISABLED_CONTAMINANT:
+ return "disabled-contaminant";
+ case USB_DATA_STATUS_DISABLED_DOCK:
+ return "disabled-dock";
+ case USB_DATA_STATUS_DISABLED_FORCE:
+ return "disabled-force";
+ case USB_DATA_STATUS_DISABLED_DEBUG:
+ return "disabled-debug";
+ default:
+ return Integer.toString(usbDataStatus);
+ }
+ }
+
+ /** @hide */
+ public static String usbDataStatusToString(int[] usbDataStatus) {
+ StringBuilder modeString = new StringBuilder();
+ if (usbDataStatus == null) {
+ return "unknown";
+ }
+ for (int i = 0; i < usbDataStatus.length; i++) {
+ modeString.append(usbDataStatusToString(usbDataStatus[i]));
+ if (i < usbDataStatus.length - 1) {
+ modeString.append(", ");
+ }
+ }
+ return modeString.toString();
+ }
+
+ /** @hide */
+ public static String powerBrickStatusToString(int powerBrickStatus) {
+ switch (powerBrickStatus) {
+ case POWER_BRICK_STATUS_UNKNOWN:
+ return "unknown";
+ case POWER_BRICK_STATUS_CONNECTED:
+ return "connected";
+ case POWER_BRICK_STATUS_DISCONNECTED:
+ return "disconnected";
+ default:
+ return Integer.toString(powerBrickStatus);
+ }
+ }
+
+ /** @hide */
public static String roleCombinationsToString(int combo) {
StringBuilder result = new StringBuilder();
result.append("[");
diff --git a/core/java/android/hardware/usb/UsbPortStatus.java b/core/java/android/hardware/usb/UsbPortStatus.java
index bb7aff651b3d..d1f424667d73 100644
--- a/core/java/android/hardware/usb/UsbPortStatus.java
+++ b/core/java/android/hardware/usb/UsbPortStatus.java
@@ -18,8 +18,8 @@ package android.hardware.usb;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
-import android.hardware.usb.V1_0.Constants;
import android.os.Parcel;
import android.os.Parcelable;
@@ -36,27 +36,31 @@ import java.lang.annotation.RetentionPolicy;
@Immutable
@SystemApi
public final class UsbPortStatus implements Parcelable {
+ private static final String TAG = "UsbPortStatus";
private final int mCurrentMode;
private final @UsbPowerRole int mCurrentPowerRole;
private final @UsbDataRole int mCurrentDataRole;
private final int mSupportedRoleCombinations;
private final @ContaminantProtectionStatus int mContaminantProtectionStatus;
private final @ContaminantDetectionStatus int mContaminantDetectionStatus;
+ private final boolean mPowerTransferLimited;
+ private final @UsbDataStatus int[] mUsbDataStatus;
+ private final @PowerBrickStatus int mPowerBrickStatus;
/**
* Power role: This USB port does not have a power role.
*/
- public static final int POWER_ROLE_NONE = Constants.PortPowerRole.NONE;
+ public static final int POWER_ROLE_NONE = 0;
/**
* Power role: This USB port can act as a source (provide power).
*/
- public static final int POWER_ROLE_SOURCE = Constants.PortPowerRole.SOURCE;
+ public static final int POWER_ROLE_SOURCE = 1;
/**
* Power role: This USB port can act as a sink (receive power).
*/
- public static final int POWER_ROLE_SINK = Constants.PortPowerRole.SINK;
+ public static final int POWER_ROLE_SINK = 2;
@IntDef(prefix = { "POWER_ROLE_" }, value = {
POWER_ROLE_NONE,
@@ -69,17 +73,17 @@ public final class UsbPortStatus implements Parcelable {
/**
* Power role: This USB port does not have a data role.
*/
- public static final int DATA_ROLE_NONE = Constants.PortDataRole.NONE;
+ public static final int DATA_ROLE_NONE = 0;
/**
* Data role: This USB port can act as a host (access data services).
*/
- public static final int DATA_ROLE_HOST = Constants.PortDataRole.HOST;
+ public static final int DATA_ROLE_HOST = 1;
/**
* Data role: This USB port can act as a device (offer data services).
*/
- public static final int DATA_ROLE_DEVICE = Constants.PortDataRole.DEVICE;
+ public static final int DATA_ROLE_DEVICE = 2;
@IntDef(prefix = { "DATA_ROLE_" }, value = {
DATA_ROLE_NONE,
@@ -92,23 +96,23 @@ public final class UsbPortStatus implements Parcelable {
/**
* There is currently nothing connected to this USB port.
*/
- public static final int MODE_NONE = Constants.PortMode.NONE;
+ public static final int MODE_NONE = 0;
/**
- * This USB port can act as a downstream facing port (host).
+ * This USB port can act as an upstream facing port (device).
*
- * <p> Implies that the port supports the {@link #POWER_ROLE_SOURCE} and
- * {@link #DATA_ROLE_HOST} combination of roles (and possibly others as well).
+ * <p> Implies that the port supports the {@link #POWER_ROLE_SINK} and
+ * {@link #DATA_ROLE_DEVICE} combination of roles (and possibly others as well).
*/
- public static final int MODE_DFP = Constants.PortMode.DFP;
+ public static final int MODE_UFP = 1 << 0;
/**
- * This USB port can act as an upstream facing port (device).
+ * This USB port can act as a downstream facing port (host).
*
- * <p> Implies that the port supports the {@link #POWER_ROLE_SINK} and
- * {@link #DATA_ROLE_DEVICE} combination of roles (and possibly others as well).
+ * <p> Implies that the port supports the {@link #POWER_ROLE_SOURCE} and
+ * {@link #DATA_ROLE_HOST} combination of roles (and possibly others as well).
*/
- public static final int MODE_UFP = Constants.PortMode.UFP;
+ public static final int MODE_DFP = 1 << 1;
/**
* This USB port can act either as an downstream facing port (host) or as
@@ -120,87 +124,127 @@ public final class UsbPortStatus implements Parcelable {
*
* @hide
*/
- public static final int MODE_DUAL = Constants.PortMode.DRP;
+ public static final int MODE_DUAL = MODE_UFP | MODE_DFP;
/**
* This USB port can support USB Type-C Audio accessory.
*/
- public static final int MODE_AUDIO_ACCESSORY =
- android.hardware.usb.V1_1.Constants.PortMode_1_1.AUDIO_ACCESSORY;
+ public static final int MODE_AUDIO_ACCESSORY = 1 << 2;
/**
* This USB port can support USB Type-C debug accessory.
*/
- public static final int MODE_DEBUG_ACCESSORY =
- android.hardware.usb.V1_1.Constants.PortMode_1_1.DEBUG_ACCESSORY;
+ public static final int MODE_DEBUG_ACCESSORY = 1 << 3;
/**
* Contaminant presence detection not supported by the device.
* @hide
*/
- public static final int CONTAMINANT_DETECTION_NOT_SUPPORTED =
- android.hardware.usb.V1_2.Constants.ContaminantDetectionStatus.NOT_SUPPORTED;
+ public static final int CONTAMINANT_DETECTION_NOT_SUPPORTED = 0;
/**
* Contaminant presence detection supported but disabled.
* @hide
*/
- public static final int CONTAMINANT_DETECTION_DISABLED =
- android.hardware.usb.V1_2.Constants.ContaminantDetectionStatus.DISABLED;
+ public static final int CONTAMINANT_DETECTION_DISABLED = 1;
/**
* Contaminant presence enabled but not detected.
* @hide
*/
- public static final int CONTAMINANT_DETECTION_NOT_DETECTED =
- android.hardware.usb.V1_2.Constants.ContaminantDetectionStatus.NOT_DETECTED;
+ public static final int CONTAMINANT_DETECTION_NOT_DETECTED = 2;
/**
* Contaminant presence enabled and detected.
* @hide
*/
- public static final int CONTAMINANT_DETECTION_DETECTED =
- android.hardware.usb.V1_2.Constants.ContaminantDetectionStatus.DETECTED;
+ public static final int CONTAMINANT_DETECTION_DETECTED = 3;
/**
* Contaminant protection - No action performed upon detection of
* contaminant presence.
* @hide
*/
- public static final int CONTAMINANT_PROTECTION_NONE =
- android.hardware.usb.V1_2.Constants.ContaminantProtectionStatus.NONE;
+ public static final int CONTAMINANT_PROTECTION_NONE = 0;
/**
* Contaminant protection - Port is forced to sink upon detection of
* contaminant presence.
* @hide
*/
- public static final int CONTAMINANT_PROTECTION_SINK =
- android.hardware.usb.V1_2.Constants.ContaminantProtectionStatus.FORCE_SINK;
+ public static final int CONTAMINANT_PROTECTION_SINK = 1 << 0;
/**
* Contaminant protection - Port is forced to source upon detection of
* contaminant presence.
* @hide
*/
- public static final int CONTAMINANT_PROTECTION_SOURCE =
- android.hardware.usb.V1_2.Constants.ContaminantProtectionStatus.FORCE_SOURCE;
+ public static final int CONTAMINANT_PROTECTION_SOURCE = 1 << 1;
/**
* Contaminant protection - Port is disabled upon detection of
* contaminant presence.
* @hide
*/
- public static final int CONTAMINANT_PROTECTION_FORCE_DISABLE =
- android.hardware.usb.V1_2.Constants.ContaminantProtectionStatus.FORCE_DISABLE;
+ public static final int CONTAMINANT_PROTECTION_FORCE_DISABLE = 1 << 2;
/**
* Contaminant protection - Port is disabled upon detection of
* contaminant presence.
* @hide
*/
- public static final int CONTAMINANT_PROTECTION_DISABLED =
- android.hardware.usb.V1_2.Constants.ContaminantProtectionStatus.DISABLED;
+ public static final int CONTAMINANT_PROTECTION_DISABLED = 1 << 3;
+
+ /**
+ * USB data status is not known.
+ */
+ public static final int USB_DATA_STATUS_UNKNOWN = 0;
+
+ /**
+ * USB data is enabled.
+ */
+ public static final int USB_DATA_STATUS_ENABLED = 1;
+
+ /**
+ * USB data is disabled as the port is too hot.
+ */
+ public static final int USB_DATA_STATUS_DISABLED_OVERHEAT = 2;
+
+ /**
+ * USB data is disabled due to contaminated port.
+ */
+ public static final int USB_DATA_STATUS_DISABLED_CONTAMINANT = 3;
+
+ /**
+ * USB data is disabled due to docking event.
+ */
+ public static final int USB_DATA_STATUS_DISABLED_DOCK = 4;
+
+ /**
+ * USB data is disabled by
+ * {@link UsbPort#enableUsbData UsbPort.enableUsbData}.
+ */
+ public static final int USB_DATA_STATUS_DISABLED_FORCE = 5;
+
+ /**
+ * USB data is disabled for debug.
+ */
+ public static final int USB_DATA_STATUS_DISABLED_DEBUG = 6;
+
+ /**
+ * Unknown whether a power brick is connected.
+ */
+ public static final int POWER_BRICK_STATUS_UNKNOWN = 0;
+
+ /**
+ * The connected device is a power brick.
+ */
+ public static final int POWER_BRICK_STATUS_CONNECTED = 1;
+
+ /**
+ * The connected device is not power brick.
+ */
+ public static final int POWER_BRICK_STATUS_DISCONNECTED = 2;
@IntDef(prefix = { "CONTAMINANT_DETECTION_" }, value = {
CONTAMINANT_DETECTION_NOT_SUPPORTED,
@@ -232,6 +276,44 @@ public final class UsbPortStatus implements Parcelable {
@interface UsbPortMode{}
/** @hide */
+ @IntDef(prefix = { "USB_DATA_STATUS_" }, value = {
+ USB_DATA_STATUS_UNKNOWN,
+ USB_DATA_STATUS_ENABLED,
+ USB_DATA_STATUS_DISABLED_OVERHEAT,
+ USB_DATA_STATUS_DISABLED_CONTAMINANT,
+ USB_DATA_STATUS_DISABLED_DOCK,
+ USB_DATA_STATUS_DISABLED_FORCE,
+ USB_DATA_STATUS_DISABLED_DEBUG
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface UsbDataStatus{}
+
+ /** @hide */
+ @IntDef(prefix = { "POWER_BRICK_STATUS_" }, value = {
+ POWER_BRICK_STATUS_UNKNOWN,
+ POWER_BRICK_STATUS_DISCONNECTED,
+ POWER_BRICK_STATUS_CONNECTED,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface PowerBrickStatus{}
+
+ /** @hide */
+ public UsbPortStatus(int currentMode, int currentPowerRole, int currentDataRole,
+ int supportedRoleCombinations, int contaminantProtectionStatus,
+ int contaminantDetectionStatus, @UsbDataStatus int[] usbDataStatus,
+ boolean powerTransferLimited, @PowerBrickStatus int powerBrickStatus) {
+ mCurrentMode = currentMode;
+ mCurrentPowerRole = currentPowerRole;
+ mCurrentDataRole = currentDataRole;
+ mSupportedRoleCombinations = supportedRoleCombinations;
+ mContaminantProtectionStatus = contaminantProtectionStatus;
+ mContaminantDetectionStatus = contaminantDetectionStatus;
+ mUsbDataStatus = usbDataStatus;
+ mPowerTransferLimited = powerTransferLimited;
+ mPowerBrickStatus = powerBrickStatus;
+ }
+
+ /** @hide */
public UsbPortStatus(int currentMode, int currentPowerRole, int currentDataRole,
int supportedRoleCombinations, int contaminantProtectionStatus,
int contaminantDetectionStatus) {
@@ -241,6 +323,9 @@ public final class UsbPortStatus implements Parcelable {
mSupportedRoleCombinations = supportedRoleCombinations;
mContaminantProtectionStatus = contaminantProtectionStatus;
mContaminantDetectionStatus = contaminantDetectionStatus;
+ mUsbDataStatus = new int[]{USB_DATA_STATUS_UNKNOWN};
+ mPowerBrickStatus = POWER_BRICK_STATUS_UNKNOWN;
+ mPowerTransferLimited = false;
}
/**
@@ -323,6 +408,40 @@ public final class UsbPortStatus implements Parcelable {
return mContaminantProtectionStatus;
}
+ /**
+ * Returns UsbData status.
+ *
+ * @return Current USB data status of the port: {@link #USB_DATA_STATUS_UNKNOWN}
+ * or {@link #USB_DATA_STATUS_ENABLED} or {@link #USB_DATA_STATUS_DIASBLED_OVERHEAT}
+ * or {@link #USB_DATA_STATUS_DISABLED_CONTAMINANT}
+ * or {@link #USB_DATA_STATUS_DISABLED_DOCK} or {@link #USB_DATA_STATUS_DISABLED_FORCE}
+ * or {@link #USB_DATA_STATUS_DISABLED_DEBUG}
+ */
+ public @UsbDataStatus @Nullable int[] getUsbDataStatus() {
+ return mUsbDataStatus;
+ }
+
+ /**
+ * Returns whether power transfer is limited.
+ *
+ * @return true when power transfer is limited.
+ * false otherwise.
+ */
+ public boolean isPowerTransferLimited() {
+ return mPowerTransferLimited;
+ }
+
+ /**
+ * Let's the caller know if a power brick is connected to the USB port.
+ *
+ * @return {@link #POWER_BRICK_STATUS_UNKNOWN}
+ * or {@link #POWER_BRICK_STATUS_CONNECTED}
+ * or {@link #POWER_BRICK_STATUS_DISCONNECTED}
+ */
+ public @PowerBrickStatus int getPowerBrickStatus() {
+ return mPowerBrickStatus;
+ }
+
@NonNull
@Override
public String toString() {
@@ -336,6 +455,12 @@ public final class UsbPortStatus implements Parcelable {
+ getContaminantDetectionStatus()
+ ", contaminantProtectionStatus="
+ getContaminantProtectionStatus()
+ + ", usbDataStatus="
+ + UsbPort.usbDataStatusToString(getUsbDataStatus())
+ + ", isPowerTransferLimited="
+ + isPowerTransferLimited()
+ +", powerBrickStatus="
+ + UsbPort.powerBrickStatusToString(getPowerBrickStatus())
+ "}";
}
@@ -352,6 +477,10 @@ public final class UsbPortStatus implements Parcelable {
dest.writeInt(mSupportedRoleCombinations);
dest.writeInt(mContaminantProtectionStatus);
dest.writeInt(mContaminantDetectionStatus);
+ dest.writeInt(mUsbDataStatus.length);
+ dest.writeIntArray(mUsbDataStatus);
+ dest.writeBoolean(mPowerTransferLimited);
+ dest.writeInt(mPowerBrickStatus);
}
public static final @NonNull Parcelable.Creator<UsbPortStatus> CREATOR =
@@ -364,9 +493,14 @@ public final class UsbPortStatus implements Parcelable {
int supportedRoleCombinations = in.readInt();
int contaminantProtectionStatus = in.readInt();
int contaminantDetectionStatus = in.readInt();
+ int[] usbDataStatus = new int[in.readInt()];
+ in.readIntArray(usbDataStatus);
+ boolean powerTransferLimited = in.readBoolean();
+ int powerBrickStatus = in.readInt();
return new UsbPortStatus(currentMode, currentPowerRole, currentDataRole,
supportedRoleCombinations, contaminantProtectionStatus,
- contaminantDetectionStatus);
+ contaminantDetectionStatus, usbDataStatus, powerTransferLimited,
+ powerBrickStatus);
}
@Override
diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java
index 6f15588c0724..cc325cde1f41 100644
--- a/core/java/android/inputmethodservice/IInputMethodWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java
@@ -72,7 +72,6 @@ class IInputMethodWrapper extends IInputMethod.Stub
private static final int DO_START_INPUT = 32;
private static final int DO_CREATE_SESSION = 40;
private static final int DO_SET_SESSION_ENABLED = 45;
- private static final int DO_REVOKE_SESSION = 50;
private static final int DO_SHOW_SOFT_INPUT = 60;
private static final int DO_HIDE_SOFT_INPUT = 70;
private static final int DO_CHANGE_INPUTMETHOD_SUBTYPE = 80;
@@ -215,9 +214,6 @@ class IInputMethodWrapper extends IInputMethod.Stub
inputMethod.setSessionEnabled((InputMethodSession)msg.obj,
msg.arg1 != 0);
return;
- case DO_REVOKE_SESSION:
- inputMethod.revokeSession((InputMethodSession)msg.obj);
- return;
case DO_SHOW_SOFT_INPUT: {
final SomeArgs args = (SomeArgs)msg.obj;
inputMethod.showSoftInputWithToken(
@@ -368,22 +364,6 @@ class IInputMethodWrapper extends IInputMethod.Stub
@BinderThread
@Override
- public void revokeSession(IInputMethodSession session) {
- try {
- InputMethodSession ls = ((IInputMethodSessionWrapper)
- session).getInternalInputMethodSession();
- if (ls == null) {
- Log.w(TAG, "Session is already finished: " + session);
- return;
- }
- mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_REVOKE_SESSION, ls));
- } catch (ClassCastException e) {
- Log.w(TAG, "Incoming session not of correct type: " + session, e);
- }
- }
-
- @BinderThread
- @Override
public void showSoftInput(IBinder showInputToken, int flags, ResultReceiver resultReceiver) {
mCaller.executeOrSendMessage(mCaller.obtainMessageIOO(DO_SHOW_SOFT_INPUT,
flags, showInputToken, resultReceiver));
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 09d50850788b..5d2d8eafb3a8 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -469,6 +469,10 @@ public class InputMethodService extends AbstractInputMethodService {
InputMethodManager mImm;
private InputMethodPrivilegedOperations mPrivOps = new InputMethodPrivilegedOperations();
+ @NonNull
+ private final NavigationBarController mNavigationBarController =
+ new NavigationBarController(this);
+
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
int mTheme = 0;
@@ -611,6 +615,7 @@ public class InputMethodService extends AbstractInputMethodService {
info.touchableRegion.set(mTmpInsets.touchableRegion);
info.setTouchableInsets(mTmpInsets.touchableInsets);
}
+ mNavigationBarController.updateTouchableInsets(mTmpInsets, info);
if (mInputFrame != null) {
setImeExclusionRect(mTmpInsets.visibleTopInsets);
@@ -1534,6 +1539,7 @@ public class InputMethodService extends AbstractInputMethodService {
mCandidatesVisibility = getCandidatesHiddenVisibility();
mCandidatesFrame.setVisibility(mCandidatesVisibility);
mInputFrame.setVisibility(View.GONE);
+ mNavigationBarController.onViewInitialized();
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
@@ -1543,6 +1549,7 @@ public class InputMethodService extends AbstractInputMethodService {
mRootView.getViewTreeObserver().removeOnComputeInternalInsetsListener(
mInsetsComputer);
doFinishInput();
+ mNavigationBarController.onDestroy();
mWindow.dismissForDestroyIfNecessary();
if (mSettingsObserver != null) {
mSettingsObserver.unregister();
@@ -2451,6 +2458,7 @@ public class InputMethodService extends AbstractInputMethodService {
setImeWindowStatus(nextImeWindowStatus, mBackDisposition);
}
+ mNavigationBarController.onWindowShown();
// compute visibility
onWindowShown();
mWindowVisible = true;
@@ -3656,6 +3664,7 @@ public class InputMethodService extends AbstractInputMethodService {
+ " touchableInsets=" + mTmpInsets.touchableInsets
+ " touchableRegion=" + mTmpInsets.touchableRegion);
p.println(" mSettingsObserver=" + mSettingsObserver);
+ p.println(" mNavigationBarController=" + mNavigationBarController.toDebugString());
}
private final ImeTracing.ServiceDumper mDumper = new ImeTracing.ServiceDumper() {
diff --git a/core/java/android/inputmethodservice/NavigationBarController.java b/core/java/android/inputmethodservice/NavigationBarController.java
new file mode 100644
index 000000000000..7295b72c276b
--- /dev/null
+++ b/core/java/android/inputmethodservice/NavigationBarController.java
@@ -0,0 +1,328 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.inputmethodservice;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.StatusBarManager;
+import android.content.res.Resources;
+import android.graphics.Insets;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.inputmethodservice.navigationbar.NavigationBarFrame;
+import android.inputmethodservice.navigationbar.NavigationBarView;
+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.WindowManagerPolicyConstants;
+import android.widget.FrameLayout;
+
+import java.util.Objects;
+
+/**
+ * This class hides details behind {@link InputMethodService#canImeRenderGesturalNavButtons()} from
+ * {@link InputMethodService}.
+ *
+ * <p>All the package-private methods are no-op when
+ * {@link InputMethodService#canImeRenderGesturalNavButtons()} returns {@code false}.</p>
+ */
+final class NavigationBarController {
+
+ private interface Callback {
+ default void updateTouchableInsets(@NonNull InputMethodService.Insets originalInsets,
+ @NonNull ViewTreeObserver.InternalInsetsInfo dest) {
+ }
+
+ default void onViewInitialized() {
+ }
+
+ default void onWindowShown() {
+ }
+
+ default void onDestroy() {
+ }
+
+ default String toDebugString() {
+ return "No-op implementation";
+ }
+
+ Callback NOOP = new Callback() {
+ };
+ }
+
+ private final Callback mImpl;
+
+ NavigationBarController(@NonNull InputMethodService inputMethodService) {
+ mImpl = InputMethodService.canImeRenderGesturalNavButtons()
+ ? new Impl(inputMethodService) : Callback.NOOP;
+ }
+
+ void updateTouchableInsets(@NonNull InputMethodService.Insets originalInsets,
+ @NonNull ViewTreeObserver.InternalInsetsInfo dest) {
+ mImpl.updateTouchableInsets(originalInsets, dest);
+ }
+
+ void onViewInitialized() {
+ mImpl.onViewInitialized();
+ }
+
+ void onWindowShown() {
+ mImpl.onWindowShown();
+ }
+
+ void onDestroy() {
+ mImpl.onDestroy();
+ }
+
+ String toDebugString() {
+ return mImpl.toDebugString();
+ }
+
+ private static final class Impl implements Callback {
+ @NonNull
+ private final InputMethodService mService;
+
+ private boolean mDestroyed = false;
+
+ private boolean mRenderGesturalNavButtons;
+
+ @Nullable
+ private NavigationBarFrame mNavigationBarFrame;
+ @Nullable
+ Insets mLastInsets;
+
+ Impl(@NonNull InputMethodService inputMethodService) {
+ mService = inputMethodService;
+ }
+
+ @Nullable
+ private Insets getSystemInsets() {
+ if (mService.mWindow == null) {
+ return null;
+ }
+ final View decorView = mService.mWindow.getWindow().getDecorView();
+ if (decorView == null) {
+ return null;
+ }
+ final WindowInsets windowInsets = decorView.getRootWindowInsets();
+ if (windowInsets == null) {
+ return null;
+ }
+ final Insets stableBarInsets =
+ windowInsets.getInsetsIgnoringVisibility(WindowInsets.Type.systemBars());
+ return Insets.min(windowInsets.getInsets(WindowInsets.Type.systemBars()
+ | WindowInsets.Type.displayCutout()), stableBarInsets);
+ }
+
+ private void installNavigationBarFrameIfNecessary() {
+ if (!mRenderGesturalNavButtons) {
+ return;
+ }
+ final View rawDecorView = mService.mWindow.getWindow().getDecorView();
+ if (!(rawDecorView instanceof ViewGroup)) {
+ return;
+ }
+ final ViewGroup decorView = (ViewGroup) rawDecorView;
+ mNavigationBarFrame = decorView.findViewByPredicate(
+ NavigationBarFrame.class::isInstance);
+ final Insets systemInsets = getSystemInsets();
+ if (mNavigationBarFrame == null) {
+ mNavigationBarFrame = new NavigationBarFrame(mService);
+ LayoutInflater.from(mService).inflate(
+ com.android.internal.R.layout.input_method_navigation_bar,
+ mNavigationBarFrame);
+ if (systemInsets != null) {
+ decorView.addView(mNavigationBarFrame, new FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ systemInsets.bottom, Gravity.BOTTOM));
+ mLastInsets = systemInsets;
+ } else {
+ decorView.addView(mNavigationBarFrame);
+ }
+ final NavigationBarView navigationBarView = mNavigationBarFrame.findViewByPredicate(
+ NavigationBarView.class::isInstance);
+ if (navigationBarView != null) {
+ // TODO(b/213337792): Support InputMethodService#setBackDisposition().
+ // TODO(b/213337792): Set NAVIGATION_HINT_IME_SHOWN only when necessary.
+ final int hints = StatusBarManager.NAVIGATION_HINT_BACK_ALT
+ | StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
+ navigationBarView.setNavigationIconHints(hints);
+ }
+ } else {
+ mNavigationBarFrame.setLayoutParams(new FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, systemInsets.bottom, Gravity.BOTTOM));
+ mLastInsets = systemInsets;
+ }
+
+ mNavigationBarFrame.setBackground(null);
+ }
+
+ @Override
+ public void updateTouchableInsets(@NonNull InputMethodService.Insets originalInsets,
+ @NonNull ViewTreeObserver.InternalInsetsInfo dest) {
+ if (!mRenderGesturalNavButtons || mNavigationBarFrame == null
+ || mService.isExtractViewShown()) {
+ return;
+ }
+
+ final Insets systemInsets = getSystemInsets();
+ if (systemInsets != null) {
+ final Window window = mService.mWindow.getWindow();
+ final View decor = window.getDecorView();
+ Region touchableRegion = null;
+ final View inputFrame = mService.mInputFrame;
+ switch (originalInsets.touchableInsets) {
+ case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME:
+ if (inputFrame.getVisibility() == View.VISIBLE) {
+ touchableRegion = new Region(inputFrame.getLeft(),
+ inputFrame.getTop(), inputFrame.getRight(),
+ inputFrame.getBottom());
+ }
+ break;
+ case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT:
+ if (inputFrame.getVisibility() == View.VISIBLE) {
+ touchableRegion = new Region(inputFrame.getLeft(),
+ originalInsets.contentTopInsets, inputFrame.getRight(),
+ inputFrame.getBottom());
+ }
+ break;
+ case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE:
+ if (inputFrame.getVisibility() == View.VISIBLE) {
+ touchableRegion = new Region(inputFrame.getLeft(),
+ originalInsets.visibleTopInsets, inputFrame.getRight(),
+ inputFrame.getBottom());
+ }
+ break;
+ case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION:
+ touchableRegion = new Region();
+ touchableRegion.set(originalInsets.touchableRegion);
+ break;
+ }
+ final Rect navBarRect = new Rect(decor.getLeft(),
+ decor.getBottom() - systemInsets.bottom,
+ decor.getRight(), decor.getBottom());
+ if (touchableRegion == null) {
+ touchableRegion = new Region(navBarRect);
+ } else {
+ touchableRegion.union(navBarRect);
+ }
+
+ dest.touchableRegion.set(touchableRegion);
+ dest.setTouchableInsets(
+ ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
+
+ // TODO(b/205803355): See if we can use View#OnLayoutChangeListener().
+ // TODO(b/205803355): See if we can replace DecorView#mNavigationColorViewState.view
+ boolean zOrderChanged = false;
+ if (decor instanceof ViewGroup) {
+ ViewGroup decorGroup = (ViewGroup) decor;
+ final View navbarBackgroundView = window.getNavigationBarBackgroundView();
+ zOrderChanged = navbarBackgroundView != null
+ && decorGroup.indexOfChild(navbarBackgroundView)
+ > decorGroup.indexOfChild(mNavigationBarFrame);
+ }
+ final boolean insetChanged = !Objects.equals(systemInsets, mLastInsets);
+ if (zOrderChanged || insetChanged) {
+ final NavigationBarFrame that = mNavigationBarFrame;
+ that.post(() -> {
+ if (!that.isAttachedToWindow()) {
+ return;
+ }
+ final Insets currentSystemInsets = getSystemInsets();
+ if (!Objects.equals(currentSystemInsets, mLastInsets)) {
+ that.setLayoutParams(
+ new FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ currentSystemInsets.bottom, Gravity.BOTTOM));
+ mLastInsets = currentSystemInsets;
+ }
+ if (decor instanceof ViewGroup) {
+ ViewGroup decorGroup = (ViewGroup) decor;
+ final View navbarBackgroundView =
+ window.getNavigationBarBackgroundView();
+ if (navbarBackgroundView != null
+ && decorGroup.indexOfChild(navbarBackgroundView)
+ > decorGroup.indexOfChild(that)) {
+ decorGroup.bringChildToFront(that);
+ }
+ }
+ });
+ }
+ }
+ }
+
+ private boolean isGesturalNavigationEnabled() {
+ final Resources resources = mService.getResources();
+ if (resources == null) {
+ return false;
+ }
+ return resources.getInteger(com.android.internal.R.integer.config_navBarInteractionMode)
+ == WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
+ }
+
+ @Override
+ public void onViewInitialized() {
+ if (mDestroyed) {
+ return;
+ }
+ mRenderGesturalNavButtons = isGesturalNavigationEnabled();
+ installNavigationBarFrameIfNecessary();
+ }
+
+ @Override
+ public void onDestroy() {
+ mDestroyed = true;
+ }
+
+ @Override
+ public void onWindowShown() {
+ if (mDestroyed || !mRenderGesturalNavButtons || mNavigationBarFrame == null) {
+ return;
+ }
+ final Insets systemInsets = getSystemInsets();
+ if (systemInsets != null) {
+ if (!Objects.equals(systemInsets, mLastInsets)) {
+ mNavigationBarFrame.setLayoutParams(new NavigationBarFrame.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ systemInsets.bottom, Gravity.BOTTOM));
+ mLastInsets = systemInsets;
+ }
+ final Window window = mService.mWindow.getWindow();
+ View rawDecorView = window.getDecorView();
+ if (rawDecorView instanceof ViewGroup) {
+ final ViewGroup decor = (ViewGroup) rawDecorView;
+ final View navbarBackgroundView = window.getNavigationBarBackgroundView();
+ if (navbarBackgroundView != null
+ && decor.indexOfChild(navbarBackgroundView)
+ > decor.indexOfChild(mNavigationBarFrame)) {
+ decor.bringChildToFront(mNavigationBarFrame);
+ }
+ }
+ mNavigationBarFrame.setVisibility(View.VISIBLE);
+ }
+ }
+
+ @Override
+ public String toDebugString() {
+ return "{mRenderGesturalNavButtons=" + mRenderGesturalNavButtons + "}";
+ }
+ }
+}
diff --git a/core/java/android/inputmethodservice/navigationbar/ButtonDispatcher.java b/core/java/android/inputmethodservice/navigationbar/ButtonDispatcher.java
new file mode 100644
index 000000000000..3f26fa461097
--- /dev/null
+++ b/core/java/android/inputmethodservice/navigationbar/ButtonDispatcher.java
@@ -0,0 +1,302 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.inputmethodservice.navigationbar;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.view.View;
+import android.view.View.AccessibilityDelegate;
+import android.view.animation.Interpolator;
+import android.view.animation.LinearInterpolator;
+
+import java.util.ArrayList;
+
+/**
+ * Dispatches common view calls to multiple views. This is used to handle
+ * multiples of the same nav bar icon appearing.
+ */
+final class ButtonDispatcher {
+ private static final int FADE_DURATION_IN = 150;
+ private static final int FADE_DURATION_OUT = 250;
+ public static final Interpolator LINEAR = new LinearInterpolator();
+
+ private final ArrayList<View> mViews = new ArrayList<>();
+
+ private final int mId;
+
+ private View.OnClickListener mClickListener;
+ private View.OnTouchListener mTouchListener;
+ private View.OnLongClickListener mLongClickListener;
+ private View.OnHoverListener mOnHoverListener;
+ private Boolean mLongClickable;
+ private float mAlpha = 1.0f;
+ private Float mDarkIntensity;
+ private int mVisibility = View.VISIBLE;
+ private Boolean mDelayTouchFeedback;
+ private KeyButtonDrawable mImageDrawable;
+ private View mCurrentView;
+ private ValueAnimator mFadeAnimator;
+ private AccessibilityDelegate mAccessibilityDelegate;
+
+ private final ValueAnimator.AnimatorUpdateListener mAlphaListener = animation ->
+ setAlpha(
+ (float) animation.getAnimatedValue(),
+ false /* animate */,
+ false /* cancelAnimator */);
+
+ private final AnimatorListenerAdapter mFadeListener = new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mFadeAnimator = null;
+ setVisibility(getAlpha() == 1 ? View.VISIBLE : View.INVISIBLE);
+ }
+ };
+
+ public ButtonDispatcher(int id) {
+ mId = id;
+ }
+
+ public void clear() {
+ mViews.clear();
+ }
+
+ public void addView(View view) {
+ mViews.add(view);
+ view.setOnClickListener(mClickListener);
+ view.setOnTouchListener(mTouchListener);
+ view.setOnLongClickListener(mLongClickListener);
+ view.setOnHoverListener(mOnHoverListener);
+ if (mLongClickable != null) {
+ view.setLongClickable(mLongClickable);
+ }
+ view.setAlpha(mAlpha);
+ view.setVisibility(mVisibility);
+ if (mAccessibilityDelegate != null) {
+ view.setAccessibilityDelegate(mAccessibilityDelegate);
+ }
+ if (view instanceof ButtonInterface) {
+ final ButtonInterface button = (ButtonInterface) view;
+ if (mDarkIntensity != null) {
+ button.setDarkIntensity(mDarkIntensity);
+ }
+ if (mImageDrawable != null) {
+ button.setImageDrawable(mImageDrawable);
+ }
+ if (mDelayTouchFeedback != null) {
+ button.setDelayTouchFeedback(mDelayTouchFeedback);
+ }
+ }
+ }
+
+ public int getId() {
+ return mId;
+ }
+
+ public int getVisibility() {
+ return mVisibility;
+ }
+
+ public boolean isVisible() {
+ return getVisibility() == View.VISIBLE;
+ }
+
+ public float getAlpha() {
+ return mAlpha;
+ }
+
+ public KeyButtonDrawable getImageDrawable() {
+ return mImageDrawable;
+ }
+
+ public void setImageDrawable(KeyButtonDrawable drawable) {
+ mImageDrawable = drawable;
+ final int N = mViews.size();
+ for (int i = 0; i < N; i++) {
+ if (mViews.get(i) instanceof ButtonInterface) {
+ ((ButtonInterface) mViews.get(i)).setImageDrawable(mImageDrawable);
+ }
+ }
+ if (mImageDrawable != null) {
+ mImageDrawable.setCallback(mCurrentView);
+ }
+ }
+
+ public void setVisibility(int visibility) {
+ if (mVisibility == visibility) return;
+ if (mFadeAnimator != null) {
+ mFadeAnimator.cancel();
+ }
+
+ mVisibility = visibility;
+ final int N = mViews.size();
+ for (int i = 0; i < N; i++) {
+ mViews.get(i).setVisibility(mVisibility);
+ }
+ }
+
+ public void setAlpha(float alpha) {
+ setAlpha(alpha, false /* animate */);
+ }
+
+ public void setAlpha(float alpha, boolean animate) {
+ setAlpha(alpha, animate, true /* cancelAnimator */);
+ }
+
+ public void setAlpha(float alpha, boolean animate, long duration) {
+ setAlpha(alpha, animate, duration, true /* cancelAnimator */);
+ }
+
+ public void setAlpha(float alpha, boolean animate, boolean cancelAnimator) {
+ setAlpha(
+ alpha,
+ animate,
+ (getAlpha() < alpha) ? FADE_DURATION_IN : FADE_DURATION_OUT,
+ cancelAnimator);
+ }
+
+ public void setAlpha(float alpha, boolean animate, long duration, boolean cancelAnimator) {
+ if (mFadeAnimator != null && (cancelAnimator || animate)) {
+ mFadeAnimator.cancel();
+ }
+ if (animate) {
+ setVisibility(View.VISIBLE);
+ mFadeAnimator = ValueAnimator.ofFloat(getAlpha(), alpha);
+ mFadeAnimator.setDuration(duration);
+ mFadeAnimator.setInterpolator(LINEAR);
+ mFadeAnimator.addListener(mFadeListener);
+ mFadeAnimator.addUpdateListener(mAlphaListener);
+ mFadeAnimator.start();
+ } else {
+ // Discretize the alpha updates to prevent too frequent updates when there is a long
+ // alpha animation
+ int prevAlpha = (int) (getAlpha() * 255);
+ int nextAlpha = (int) (alpha * 255);
+ if (prevAlpha != nextAlpha) {
+ mAlpha = nextAlpha / 255f;
+ final int N = mViews.size();
+ for (int i = 0; i < N; i++) {
+ mViews.get(i).setAlpha(mAlpha);
+ }
+ }
+ }
+ }
+
+ public void setDarkIntensity(float darkIntensity) {
+ mDarkIntensity = darkIntensity;
+ final int N = mViews.size();
+ for (int i = 0; i < N; i++) {
+ if (mViews.get(i) instanceof ButtonInterface) {
+ ((ButtonInterface) mViews.get(i)).setDarkIntensity(darkIntensity);
+ }
+ }
+ }
+
+ public void setDelayTouchFeedback(boolean delay) {
+ mDelayTouchFeedback = delay;
+ final int N = mViews.size();
+ for (int i = 0; i < N; i++) {
+ if (mViews.get(i) instanceof ButtonInterface) {
+ ((ButtonInterface) mViews.get(i)).setDelayTouchFeedback(delay);
+ }
+ }
+ }
+
+ public void setOnClickListener(View.OnClickListener clickListener) {
+ mClickListener = clickListener;
+ final int N = mViews.size();
+ for (int i = 0; i < N; i++) {
+ mViews.get(i).setOnClickListener(mClickListener);
+ }
+ }
+
+ public void setOnTouchListener(View.OnTouchListener touchListener) {
+ mTouchListener = touchListener;
+ final int N = mViews.size();
+ for (int i = 0; i < N; i++) {
+ mViews.get(i).setOnTouchListener(mTouchListener);
+ }
+ }
+
+ public void setLongClickable(boolean isLongClickable) {
+ mLongClickable = isLongClickable;
+ final int N = mViews.size();
+ for (int i = 0; i < N; i++) {
+ mViews.get(i).setLongClickable(mLongClickable);
+ }
+ }
+
+ public void setOnLongClickListener(View.OnLongClickListener longClickListener) {
+ mLongClickListener = longClickListener;
+ final int N = mViews.size();
+ for (int i = 0; i < N; i++) {
+ mViews.get(i).setOnLongClickListener(mLongClickListener);
+ }
+ }
+
+ public void setOnHoverListener(View.OnHoverListener hoverListener) {
+ mOnHoverListener = hoverListener;
+ final int N = mViews.size();
+ for (int i = 0; i < N; i++) {
+ mViews.get(i).setOnHoverListener(mOnHoverListener);
+ }
+ }
+
+ public void setAccessibilityDelegate(AccessibilityDelegate delegate) {
+ mAccessibilityDelegate = delegate;
+ final int N = mViews.size();
+ for (int i = 0; i < N; i++) {
+ mViews.get(i).setAccessibilityDelegate(delegate);
+ }
+ }
+
+ public void setTranslation(int x, int y, int z) {
+ final int N = mViews.size();
+ for (int i = 0; i < N; i++) {
+ final View view = mViews.get(i);
+ view.setTranslationX(x);
+ view.setTranslationY(y);
+ view.setTranslationZ(z);
+ }
+ }
+
+ public ArrayList<View> getViews() {
+ return mViews;
+ }
+
+ public View getCurrentView() {
+ return mCurrentView;
+ }
+
+ public void setCurrentView(View currentView) {
+ mCurrentView = currentView.findViewById(mId);
+ if (mImageDrawable != null) {
+ mImageDrawable.setCallback(mCurrentView);
+ }
+ if (mCurrentView != null) {
+ mCurrentView.setTranslationX(0);
+ mCurrentView.setTranslationY(0);
+ mCurrentView.setTranslationZ(0);
+ }
+ }
+
+ /**
+ * Executes when button is detached from window.
+ */
+ public void onDestroy() {
+ }
+}
diff --git a/core/java/android/inputmethodservice/navigationbar/ButtonInterface.java b/core/java/android/inputmethodservice/navigationbar/ButtonInterface.java
new file mode 100644
index 000000000000..1c9c86d2a7e5
--- /dev/null
+++ b/core/java/android/inputmethodservice/navigationbar/ButtonInterface.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.inputmethodservice.navigationbar;
+
+import android.annotation.Nullable;
+import android.graphics.drawable.Drawable;
+
+interface ButtonInterface {
+
+ void setImageDrawable(@Nullable Drawable drawable);
+
+ void setDarkIntensity(float intensity);
+
+ void setDelayTouchFeedback(boolean shouldDelay);
+}
diff --git a/core/java/android/inputmethodservice/navigationbar/DeadZone.java b/core/java/android/inputmethodservice/navigationbar/DeadZone.java
new file mode 100644
index 000000000000..cd857369bc5a
--- /dev/null
+++ b/core/java/android/inputmethodservice/navigationbar/DeadZone.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.inputmethodservice.navigationbar;
+
+import static android.inputmethodservice.navigationbar.NavigationBarConstants.NAVIGATION_BAR_DEADZONE_DECAY;
+import static android.inputmethodservice.navigationbar.NavigationBarConstants.NAVIGATION_BAR_DEADZONE_HOLD;
+import static android.inputmethodservice.navigationbar.NavigationBarConstants.NAVIGATION_BAR_DEADZONE_SIZE;
+import static android.inputmethodservice.navigationbar.NavigationBarConstants.NAVIGATION_BAR_DEADZONE_SIZE_MAX;
+import static android.inputmethodservice.navigationbar.NavigationBarUtils.dpToPx;
+
+import android.animation.ObjectAnimator;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.os.SystemClock;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.Surface;
+
+/**
+ * The "dead zone" consumes unintentional taps along the top edge of the navigation bar.
+ * When users are typing quickly on an IME they may attempt to hit the space bar, overshoot, and
+ * accidentally hit the home button. The DeadZone expands temporarily after each tap in the UI
+ * outside the navigation bar (since this is when accidental taps are more likely), then contracts
+ * back over time (since a later tap might be intended for the top of the bar).
+ */
+final class DeadZone {
+ public static final String TAG = "DeadZone";
+
+ public static final boolean DEBUG = false;
+ public static final int HORIZONTAL = 0; // Consume taps along the top edge.
+ public static final int VERTICAL = 1; // Consume taps along the left edge.
+
+ private static final boolean CHATTY = true; // print to logcat when we eat a click
+ private final NavigationBarView mNavigationBarView;
+
+ private boolean mShouldFlash;
+ private float mFlashFrac = 0f;
+
+ private int mSizeMax;
+ private int mSizeMin;
+ // Upon activity elsewhere in the UI, the dead zone will hold steady for
+ // mHold ms, then move back over the course of mDecay ms
+ private int mHold, mDecay;
+ private boolean mVertical;
+ private long mLastPokeTime;
+ private int mDisplayRotation;
+
+ private final Runnable mDebugFlash = new Runnable() {
+ @Override
+ public void run() {
+ ObjectAnimator.ofFloat(DeadZone.this, "flash", 1f, 0f).setDuration(150).start();
+ }
+ };
+
+ public DeadZone(NavigationBarView view) {
+ mNavigationBarView = view;
+ onConfigurationChanged(Surface.ROTATION_0);
+ }
+
+ static float lerp(float a, float b, float f) {
+ return (b - a) * f + a;
+ }
+
+ private float getSize(long now) {
+ if (mSizeMax == 0)
+ return 0;
+ long dt = (now - mLastPokeTime);
+ if (dt > mHold + mDecay)
+ return mSizeMin;
+ if (dt < mHold)
+ return mSizeMax;
+ return (int) lerp(mSizeMax, mSizeMin, (float) (dt - mHold) / mDecay);
+ }
+
+ public void setFlashOnTouchCapture(boolean dbg) {
+ mShouldFlash = dbg;
+ mFlashFrac = 0f;
+ mNavigationBarView.postInvalidate();
+ }
+
+ public void onConfigurationChanged(int rotation) {
+ mDisplayRotation = rotation;
+
+ final Resources res = mNavigationBarView.getResources();
+ mHold = NAVIGATION_BAR_DEADZONE_HOLD;
+ mDecay = NAVIGATION_BAR_DEADZONE_DECAY;
+
+ mSizeMin = dpToPx(NAVIGATION_BAR_DEADZONE_SIZE, res);
+ mSizeMax = dpToPx(NAVIGATION_BAR_DEADZONE_SIZE_MAX, res);
+ mVertical = (res.getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE);
+
+ if (DEBUG) {
+ Log.v(TAG, this + " size=[" + mSizeMin + "-" + mSizeMax + "] hold=" + mHold
+ + (mVertical ? " vertical" : " horizontal"));
+ }
+ setFlashOnTouchCapture(false); // hard-coded from "bool/config_dead_zone_flash"
+ }
+
+ // I made you a touch event...
+ public boolean onTouchEvent(MotionEvent event) {
+ if (DEBUG) {
+ Log.v(TAG, this + " onTouch: " + MotionEvent.actionToString(event.getAction()));
+ }
+
+ // Don't consume events for high precision pointing devices. For this purpose a stylus is
+ // considered low precision (like a finger), so its events may be consumed.
+ if (event.getToolType(0) == MotionEvent.TOOL_TYPE_MOUSE) {
+ return false;
+ }
+
+ final int action = event.getAction();
+ if (action == MotionEvent.ACTION_OUTSIDE) {
+ poke(event);
+ return true;
+ } else if (action == MotionEvent.ACTION_DOWN) {
+ if (DEBUG) {
+ Log.v(TAG, this + " ACTION_DOWN: " + event.getX() + "," + event.getY());
+ }
+ //TODO(b/205803355): call mNavBarController.touchAutoDim(mDisplayId); here
+ int size = (int) getSize(event.getEventTime());
+ // In the vertical orientation consume taps along the left edge.
+ // In horizontal orientation consume taps along the top edge.
+ final boolean consumeEvent;
+ if (mVertical) {
+ if (mDisplayRotation == Surface.ROTATION_270) {
+ consumeEvent = event.getX() > mNavigationBarView.getWidth() - size;
+ } else {
+ consumeEvent = event.getX() < size;
+ }
+ } else {
+ consumeEvent = event.getY() < size;
+ }
+ if (consumeEvent) {
+ if (CHATTY) {
+ Log.v(TAG, "consuming errant click: (" + event.getX() + ","
+ + event.getY() + ")");
+ }
+ if (mShouldFlash) {
+ mNavigationBarView.post(mDebugFlash);
+ mNavigationBarView.postInvalidate();
+ }
+ return true; // ...but I eated it
+ }
+ }
+ return false;
+ }
+
+ private void poke(MotionEvent event) {
+ mLastPokeTime = event.getEventTime();
+ if (DEBUG)
+ Log.v(TAG, "poked! size=" + getSize(mLastPokeTime));
+ if (mShouldFlash) mNavigationBarView.postInvalidate();
+ }
+
+ public void setFlash(float f) {
+ mFlashFrac = f;
+ mNavigationBarView.postInvalidate();
+ }
+
+ public float getFlash() {
+ return mFlashFrac;
+ }
+
+ public void onDraw(Canvas can) {
+ if (!mShouldFlash || mFlashFrac <= 0f) {
+ return;
+ }
+
+ final int size = (int) getSize(SystemClock.uptimeMillis());
+ if (mVertical) {
+ if (mDisplayRotation == Surface.ROTATION_270) {
+ can.clipRect(can.getWidth() - size, 0, can.getWidth(), can.getHeight());
+ } else {
+ can.clipRect(0, 0, size, can.getHeight());
+ }
+ } else {
+ can.clipRect(0, 0, can.getWidth(), size);
+ }
+
+ final float frac = DEBUG ? (mFlashFrac - 0.5f) + 0.5f : mFlashFrac;
+ can.drawARGB((int) (frac * 0xFF), 0xDD, 0xEE, 0xAA);
+
+ if (DEBUG && size > mSizeMin) {
+ // Very aggressive redrawing here, for debugging only
+ mNavigationBarView.postInvalidateDelayed(100);
+ }
+ }
+}
diff --git a/core/java/android/inputmethodservice/navigationbar/KeyButtonDrawable.java b/core/java/android/inputmethodservice/navigationbar/KeyButtonDrawable.java
new file mode 100644
index 000000000000..25a443de916b
--- /dev/null
+++ b/core/java/android/inputmethodservice/navigationbar/KeyButtonDrawable.java
@@ -0,0 +1,483 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.inputmethodservice.navigationbar;
+
+import static android.inputmethodservice.navigationbar.NavigationBarConstants.NAV_KEY_BUTTON_SHADOW_COLOR;
+import static android.inputmethodservice.navigationbar.NavigationBarConstants.NAV_KEY_BUTTON_SHADOW_OFFSET_X;
+import static android.inputmethodservice.navigationbar.NavigationBarConstants.NAV_KEY_BUTTON_SHADOW_OFFSET_Y;
+import static android.inputmethodservice.navigationbar.NavigationBarConstants.NAV_KEY_BUTTON_SHADOW_RADIUS;
+import static android.inputmethodservice.navigationbar.NavigationBarUtils.dpToPx;
+
+import android.animation.ArgbEvaluator;
+import android.annotation.ColorInt;
+import android.annotation.DrawableRes;
+import android.annotation.NonNull;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BlurMaskFilter;
+import android.graphics.BlurMaskFilter.Blur;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuff.Mode;
+import android.graphics.PorterDuffColorFilter;
+import android.graphics.Rect;
+import android.graphics.drawable.AnimatedVectorDrawable;
+import android.graphics.drawable.Drawable;
+import android.util.FloatProperty;
+import android.view.View;
+
+
+/**
+ * Drawable for {@link KeyButtonView}s that supports tinting between two colors, rotation and shows
+ * a shadow. AnimatedVectorDrawable will only support tinting from intensities but has no support
+ * for shadows nor rotations.
+ */
+final class KeyButtonDrawable extends Drawable {
+
+ public static final FloatProperty<KeyButtonDrawable> KEY_DRAWABLE_ROTATE =
+ new FloatProperty<KeyButtonDrawable>("KeyButtonRotation") {
+ @Override
+ public void setValue(KeyButtonDrawable drawable, float degree) {
+ drawable.setRotation(degree);
+ }
+
+ @Override
+ public Float get(KeyButtonDrawable drawable) {
+ return drawable.getRotation();
+ }
+ };
+
+ public static final FloatProperty<KeyButtonDrawable> KEY_DRAWABLE_TRANSLATE_Y =
+ new FloatProperty<KeyButtonDrawable>("KeyButtonTranslateY") {
+ @Override
+ public void setValue(KeyButtonDrawable drawable, float y) {
+ drawable.setTranslationY(y);
+ }
+
+ @Override
+ public Float get(KeyButtonDrawable drawable) {
+ return drawable.getTranslationY();
+ }
+ };
+
+ private final Paint mIconPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
+ private final Paint mShadowPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
+ private final ShadowDrawableState mState;
+ private AnimatedVectorDrawable mAnimatedDrawable;
+ private final Callback mAnimatedDrawableCallback = new Callback() {
+ @Override
+ public void invalidateDrawable(@NonNull Drawable who) {
+ invalidateSelf();
+ }
+
+ @Override
+ public void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when) {
+ scheduleSelf(what, when);
+ }
+
+ @Override
+ public void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what) {
+ unscheduleSelf(what);
+ }
+ };
+
+ public KeyButtonDrawable(Drawable d, @ColorInt int lightColor, @ColorInt int darkColor,
+ boolean horizontalFlip, Color ovalBackgroundColor) {
+ this(d, new ShadowDrawableState(lightColor, darkColor,
+ d instanceof AnimatedVectorDrawable, horizontalFlip, ovalBackgroundColor));
+ }
+
+ private KeyButtonDrawable(Drawable d, ShadowDrawableState state) {
+ mState = state;
+ if (d != null) {
+ mState.mBaseHeight = d.getIntrinsicHeight();
+ mState.mBaseWidth = d.getIntrinsicWidth();
+ mState.mChangingConfigurations = d.getChangingConfigurations();
+ mState.mChildState = d.getConstantState();
+ }
+ if (canAnimate()) {
+ mAnimatedDrawable = (AnimatedVectorDrawable) mState.mChildState.newDrawable().mutate();
+ mAnimatedDrawable.setCallback(mAnimatedDrawableCallback);
+ setDrawableBounds(mAnimatedDrawable);
+ }
+ }
+
+ public void setDarkIntensity(float intensity) {
+ mState.mDarkIntensity = intensity;
+ final int color = (int) ArgbEvaluator.getInstance()
+ .evaluate(intensity, mState.mLightColor, mState.mDarkColor);
+ updateShadowAlpha();
+ setColorFilter(new PorterDuffColorFilter(color, Mode.SRC_ATOP));
+ }
+
+ public void setRotation(float degrees) {
+ if (canAnimate()) {
+ // AnimatedVectorDrawables will not support rotation
+ return;
+ }
+ if (mState.mRotateDegrees != degrees) {
+ mState.mRotateDegrees = degrees;
+ invalidateSelf();
+ }
+ }
+
+ public void setTranslationX(float x) {
+ setTranslation(x, mState.mTranslationY);
+ }
+
+ public void setTranslationY(float y) {
+ setTranslation(mState.mTranslationX, y);
+ }
+
+ public void setTranslation(float x, float y) {
+ if (mState.mTranslationX != x || mState.mTranslationY != y) {
+ mState.mTranslationX = x;
+ mState.mTranslationY = y;
+ invalidateSelf();
+ }
+ }
+
+ public void setShadowProperties(int x, int y, int size, int color) {
+ if (canAnimate()) {
+ // AnimatedVectorDrawables will not support shadows
+ return;
+ }
+ if (mState.mShadowOffsetX != x || mState.mShadowOffsetY != y
+ || mState.mShadowSize != size || mState.mShadowColor != color) {
+ mState.mShadowOffsetX = x;
+ mState.mShadowOffsetY = y;
+ mState.mShadowSize = size;
+ mState.mShadowColor = color;
+ mShadowPaint.setColorFilter(
+ new PorterDuffColorFilter(mState.mShadowColor, Mode.SRC_ATOP));
+ updateShadowAlpha();
+ invalidateSelf();
+ }
+ }
+
+ @Override
+ public boolean setVisible(boolean visible, boolean restart) {
+ boolean changed = super.setVisible(visible, restart);
+ if (changed) {
+ // End any existing animations when the visibility changes
+ jumpToCurrentState();
+ }
+ return changed;
+ }
+
+ @Override
+ public void jumpToCurrentState() {
+ super.jumpToCurrentState();
+ if (mAnimatedDrawable != null) {
+ mAnimatedDrawable.jumpToCurrentState();
+ }
+ }
+
+ @Override
+ public void setAlpha(int alpha) {
+ mState.mAlpha = alpha;
+ mIconPaint.setAlpha(alpha);
+ updateShadowAlpha();
+ invalidateSelf();
+ }
+
+ @Override
+ public void setColorFilter(ColorFilter colorFilter) {
+ mIconPaint.setColorFilter(colorFilter);
+ if (mAnimatedDrawable != null) {
+ if (hasOvalBg()) {
+ mAnimatedDrawable.setColorFilter(
+ new PorterDuffColorFilter(mState.mLightColor, PorterDuff.Mode.SRC_IN));
+ } else {
+ mAnimatedDrawable.setColorFilter(colorFilter);
+ }
+ }
+ invalidateSelf();
+ }
+
+ public float getDarkIntensity() {
+ return mState.mDarkIntensity;
+ }
+
+ public float getRotation() {
+ return mState.mRotateDegrees;
+ }
+
+ public float getTranslationX() {
+ return mState.mTranslationX;
+ }
+
+ public float getTranslationY() {
+ return mState.mTranslationY;
+ }
+
+ @Override
+ public ConstantState getConstantState() {
+ return mState;
+ }
+
+ @Override
+ public int getOpacity() {
+ return PixelFormat.TRANSLUCENT;
+ }
+
+ @Override
+ public int getIntrinsicHeight() {
+ return mState.mBaseHeight + (mState.mShadowSize + Math.abs(mState.mShadowOffsetY)) * 2;
+ }
+
+ @Override
+ public int getIntrinsicWidth() {
+ return mState.mBaseWidth + (mState.mShadowSize + Math.abs(mState.mShadowOffsetX)) * 2;
+ }
+
+ public boolean canAnimate() {
+ return mState.mSupportsAnimation;
+ }
+
+ public void startAnimation() {
+ if (mAnimatedDrawable != null) {
+ mAnimatedDrawable.start();
+ }
+ }
+
+ public void resetAnimation() {
+ if (mAnimatedDrawable != null) {
+ mAnimatedDrawable.reset();
+ }
+ }
+
+ public void clearAnimationCallbacks() {
+ if (mAnimatedDrawable != null) {
+ mAnimatedDrawable.clearAnimationCallbacks();
+ }
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ Rect bounds = getBounds();
+ if (bounds.isEmpty()) {
+ return;
+ }
+
+ if (mAnimatedDrawable != null) {
+ mAnimatedDrawable.draw(canvas);
+ } else {
+ // If no cache or previous cached bitmap is hardware/software acceleration does not
+ // match the current canvas on draw then regenerate
+ boolean hwBitmapChanged = mState.mIsHardwareBitmap != canvas.isHardwareAccelerated();
+ if (hwBitmapChanged) {
+ mState.mIsHardwareBitmap = canvas.isHardwareAccelerated();
+ }
+ if (mState.mLastDrawnIcon == null || hwBitmapChanged) {
+ regenerateBitmapIconCache();
+ }
+ canvas.save();
+ canvas.translate(mState.mTranslationX, mState.mTranslationY);
+ canvas.rotate(mState.mRotateDegrees, getIntrinsicWidth() / 2, getIntrinsicHeight() / 2);
+
+ if (mState.mShadowSize > 0) {
+ if (mState.mLastDrawnShadow == null || hwBitmapChanged) {
+ regenerateBitmapShadowCache();
+ }
+
+ // Translate (with rotation offset) before drawing the shadow
+ final float radians = (float) (mState.mRotateDegrees * Math.PI / 180);
+ final float shadowOffsetX = (float) (Math.sin(radians) * mState.mShadowOffsetY
+ + Math.cos(radians) * mState.mShadowOffsetX) - mState.mTranslationX;
+ final float shadowOffsetY = (float) (Math.cos(radians) * mState.mShadowOffsetY
+ - Math.sin(radians) * mState.mShadowOffsetX) - mState.mTranslationY;
+ canvas.drawBitmap(mState.mLastDrawnShadow, shadowOffsetX, shadowOffsetY,
+ mShadowPaint);
+ }
+ canvas.drawBitmap(mState.mLastDrawnIcon, null, bounds, mIconPaint);
+ canvas.restore();
+ }
+ }
+
+ @Override
+ public boolean canApplyTheme() {
+ return mState.canApplyTheme();
+ }
+
+ @ColorInt int getDrawableBackgroundColor() {
+ return mState.mOvalBackgroundColor.toArgb();
+ }
+
+ boolean hasOvalBg() {
+ return mState.mOvalBackgroundColor != null;
+ }
+
+ private void regenerateBitmapIconCache() {
+ final int width = getIntrinsicWidth();
+ final int height = getIntrinsicHeight();
+ Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ final Canvas canvas = new Canvas(bitmap);
+
+ // Call mutate, so that the pixel allocation by the underlying vector drawable is cleared.
+ final Drawable d = mState.mChildState.newDrawable().mutate();
+ setDrawableBounds(d);
+ canvas.save();
+ if (mState.mHorizontalFlip) {
+ canvas.scale(-1f, 1f, width * 0.5f, height * 0.5f);
+ }
+ d.draw(canvas);
+ canvas.restore();
+
+ if (mState.mIsHardwareBitmap) {
+ bitmap = bitmap.copy(Bitmap.Config.HARDWARE, false);
+ }
+ mState.mLastDrawnIcon = bitmap;
+ }
+
+ private void regenerateBitmapShadowCache() {
+ if (mState.mShadowSize == 0) {
+ // No shadow
+ mState.mLastDrawnIcon = null;
+ return;
+ }
+
+ final int width = getIntrinsicWidth();
+ final int height = getIntrinsicHeight();
+ Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bitmap);
+
+ // Call mutate, so that the pixel allocation by the underlying vector drawable is cleared.
+ final Drawable d = mState.mChildState.newDrawable().mutate();
+ setDrawableBounds(d);
+ canvas.save();
+ if (mState.mHorizontalFlip) {
+ canvas.scale(-1f, 1f, width * 0.5f, height * 0.5f);
+ }
+ d.draw(canvas);
+ canvas.restore();
+
+ // Draws the shadow from original drawable
+ Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
+ paint.setMaskFilter(new BlurMaskFilter(mState.mShadowSize, Blur.NORMAL));
+ int[] offset = new int[2];
+ final Bitmap shadow = bitmap.extractAlpha(paint, offset);
+ paint.setMaskFilter(null);
+ bitmap.eraseColor(Color.TRANSPARENT);
+ canvas.drawBitmap(shadow, offset[0], offset[1], paint);
+
+ if (mState.mIsHardwareBitmap) {
+ bitmap = bitmap.copy(Bitmap.Config.HARDWARE, false);
+ }
+ mState.mLastDrawnShadow = bitmap;
+ }
+
+ /**
+ * Set the alpha of the shadow. As dark intensity increases, drop the alpha of the shadow since
+ * dark color and shadow should not be visible at the same time.
+ */
+ private void updateShadowAlpha() {
+ // Update the color from the original color's alpha as the max
+ int alpha = Color.alpha(mState.mShadowColor);
+ mShadowPaint.setAlpha(
+ Math.round(alpha * (mState.mAlpha / 255f) * (1 - mState.mDarkIntensity)));
+ }
+
+ /**
+ * Prevent shadow clipping by offsetting the drawable bounds by the shadow and its offset
+ * @param d the drawable to set the bounds
+ */
+ private void setDrawableBounds(Drawable d) {
+ final int offsetX = mState.mShadowSize + Math.abs(mState.mShadowOffsetX);
+ final int offsetY = mState.mShadowSize + Math.abs(mState.mShadowOffsetY);
+ d.setBounds(offsetX, offsetY, getIntrinsicWidth() - offsetX,
+ getIntrinsicHeight() - offsetY);
+ }
+
+ private static class ShadowDrawableState extends ConstantState {
+ int mChangingConfigurations;
+ int mBaseWidth;
+ int mBaseHeight;
+ float mRotateDegrees;
+ float mTranslationX;
+ float mTranslationY;
+ int mShadowOffsetX;
+ int mShadowOffsetY;
+ int mShadowSize;
+ int mShadowColor;
+ float mDarkIntensity;
+ int mAlpha;
+ boolean mHorizontalFlip;
+
+ boolean mIsHardwareBitmap;
+ Bitmap mLastDrawnIcon;
+ Bitmap mLastDrawnShadow;
+ ConstantState mChildState;
+
+ final int mLightColor;
+ final int mDarkColor;
+ final boolean mSupportsAnimation;
+ final Color mOvalBackgroundColor;
+
+ public ShadowDrawableState(@ColorInt int lightColor, @ColorInt int darkColor,
+ boolean animated, boolean horizontalFlip, Color ovalBackgroundColor) {
+ mLightColor = lightColor;
+ mDarkColor = darkColor;
+ mSupportsAnimation = animated;
+ mAlpha = 255;
+ mHorizontalFlip = horizontalFlip;
+ mOvalBackgroundColor = ovalBackgroundColor;
+ }
+
+ @Override
+ public Drawable newDrawable() {
+ return new KeyButtonDrawable(null, this);
+ }
+
+ @Override
+ public int getChangingConfigurations() {
+ return mChangingConfigurations;
+ }
+
+ @Override
+ public boolean canApplyTheme() {
+ return true;
+ }
+ }
+
+ /**
+ * Creates a KeyButtonDrawable with a shadow given its icon. For more information, see
+ * {@link #create(Context, int, boolean, boolean)}.
+ */
+ public static KeyButtonDrawable create(Context context, @ColorInt int lightColor,
+ @ColorInt int darkColor, @DrawableRes int iconResId, boolean hasShadow,
+ Color ovalBackgroundColor) {
+ final Resources res = context.getResources();
+ boolean isRtl = res.getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
+ Drawable d = context.getDrawable(iconResId);
+ final KeyButtonDrawable drawable = new KeyButtonDrawable(d, lightColor, darkColor,
+ isRtl && d.isAutoMirrored(), ovalBackgroundColor);
+ if (hasShadow) {
+ int offsetX = dpToPx(NAV_KEY_BUTTON_SHADOW_OFFSET_X, res);
+ int offsetY = dpToPx(NAV_KEY_BUTTON_SHADOW_OFFSET_Y, res);
+ int radius = dpToPx(NAV_KEY_BUTTON_SHADOW_RADIUS, res);
+ int color = NAV_KEY_BUTTON_SHADOW_COLOR;
+ drawable.setShadowProperties(offsetX, offsetY, radius, color);
+ }
+ return drawable;
+ }
+}
diff --git a/core/java/android/inputmethodservice/navigationbar/KeyButtonRipple.java b/core/java/android/inputmethodservice/navigationbar/KeyButtonRipple.java
new file mode 100644
index 000000000000..38a63b661ac0
--- /dev/null
+++ b/core/java/android/inputmethodservice/navigationbar/KeyButtonRipple.java
@@ -0,0 +1,525 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.inputmethodservice.navigationbar;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.annotation.DimenRes;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.CanvasProperty;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.RecordingCanvas;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.Trace;
+import android.view.RenderNodeAnimator;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.animation.Interpolator;
+import android.view.animation.PathInterpolator;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+
+final class KeyButtonRipple extends Drawable {
+
+ private static final float GLOW_MAX_SCALE_FACTOR = 1.35f;
+ private static final float GLOW_MAX_ALPHA = 0.2f;
+ private static final float GLOW_MAX_ALPHA_DARK = 0.1f;
+ private static final int ANIMATION_DURATION_SCALE = 350;
+ private static final int ANIMATION_DURATION_FADE = 450;
+ private static final Interpolator ALPHA_OUT_INTERPOLATOR =
+ new PathInterpolator(0f, 0f, 0.8f, 1f);
+
+ @DimenRes
+ private final int mMaxWidthResource;
+
+ private Paint mRipplePaint;
+ private CanvasProperty<Float> mLeftProp;
+ private CanvasProperty<Float> mTopProp;
+ private CanvasProperty<Float> mRightProp;
+ private CanvasProperty<Float> mBottomProp;
+ private CanvasProperty<Float> mRxProp;
+ private CanvasProperty<Float> mRyProp;
+ private CanvasProperty<Paint> mPaintProp;
+ private float mGlowAlpha = 0f;
+ private float mGlowScale = 1f;
+ private boolean mPressed;
+ private boolean mVisible;
+ private boolean mDrawingHardwareGlow;
+ private int mMaxWidth;
+ private boolean mLastDark;
+ private boolean mDark;
+ private boolean mDelayTouchFeedback;
+
+ private final Interpolator mInterpolator = new LogInterpolator();
+ private boolean mSupportHardware;
+ private final View mTargetView;
+ private final Handler mHandler = new Handler();
+
+ private final HashSet<Animator> mRunningAnimations = new HashSet<>();
+ private final ArrayList<Animator> mTmpArray = new ArrayList<>();
+
+ private final TraceAnimatorListener mExitHwTraceAnimator =
+ new TraceAnimatorListener("exitHardware");
+ private final TraceAnimatorListener mEnterHwTraceAnimator =
+ new TraceAnimatorListener("enterHardware");
+
+ public enum Type {
+ OVAL,
+ ROUNDED_RECT
+ }
+
+ private Type mType = Type.ROUNDED_RECT;
+
+ public KeyButtonRipple(Context ctx, View targetView, @DimenRes int maxWidthResource) {
+ mMaxWidthResource = maxWidthResource;
+ mMaxWidth = ctx.getResources().getDimensionPixelSize(maxWidthResource);
+ mTargetView = targetView;
+ }
+
+ public void updateResources() {
+ mMaxWidth = mTargetView.getContext().getResources()
+ .getDimensionPixelSize(mMaxWidthResource);
+ invalidateSelf();
+ }
+
+ public void setDarkIntensity(float darkIntensity) {
+ mDark = darkIntensity >= 0.5f;
+ }
+
+ public void setDelayTouchFeedback(boolean delay) {
+ mDelayTouchFeedback = delay;
+ }
+
+ public void setType(Type type) {
+ mType = type;
+ }
+
+ private Paint getRipplePaint() {
+ if (mRipplePaint == null) {
+ mRipplePaint = new Paint();
+ mRipplePaint.setAntiAlias(true);
+ mRipplePaint.setColor(mLastDark ? 0xff000000 : 0xffffffff);
+ }
+ return mRipplePaint;
+ }
+
+ private void drawSoftware(Canvas canvas) {
+ if (mGlowAlpha > 0f) {
+ final Paint p = getRipplePaint();
+ p.setAlpha((int)(mGlowAlpha * 255f));
+
+ final float w = getBounds().width();
+ final float h = getBounds().height();
+ final boolean horizontal = w > h;
+ final float diameter = getRippleSize() * mGlowScale;
+ final float radius = diameter * .5f;
+ final float cx = w * .5f;
+ final float cy = h * .5f;
+ final float rx = horizontal ? radius : cx;
+ final float ry = horizontal ? cy : radius;
+ final float corner = horizontal ? cy : cx;
+
+ if (mType == Type.ROUNDED_RECT) {
+ canvas.drawRoundRect(cx - rx, cy - ry, cx + rx, cy + ry, corner, corner, p);
+ } else {
+ canvas.save();
+ canvas.translate(cx, cy);
+ float r = Math.min(rx, ry);
+ canvas.drawOval(-r, -r, r, r, p);
+ canvas.restore();
+ }
+ }
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ mSupportHardware = canvas.isHardwareAccelerated();
+ if (mSupportHardware) {
+ drawHardware((RecordingCanvas) canvas);
+ } else {
+ drawSoftware(canvas);
+ }
+ }
+
+ @Override
+ public void setAlpha(int alpha) {
+ // Not supported.
+ }
+
+ @Override
+ public void setColorFilter(ColorFilter colorFilter) {
+ // Not supported.
+ }
+
+ @Override
+ public int getOpacity() {
+ return PixelFormat.TRANSLUCENT;
+ }
+
+ private boolean isHorizontal() {
+ return getBounds().width() > getBounds().height();
+ }
+
+ private void drawHardware(RecordingCanvas c) {
+ if (mDrawingHardwareGlow) {
+ if (mType == Type.ROUNDED_RECT) {
+ c.drawRoundRect(mLeftProp, mTopProp, mRightProp, mBottomProp, mRxProp, mRyProp,
+ mPaintProp);
+ } else {
+ CanvasProperty<Float> cx = CanvasProperty.createFloat(getBounds().width() / 2);
+ CanvasProperty<Float> cy = CanvasProperty.createFloat(getBounds().height() / 2);
+ int d = Math.min(getBounds().width(), getBounds().height());
+ CanvasProperty<Float> r = CanvasProperty.createFloat(1.0f * d / 2);
+ c.drawCircle(cx, cy, r, mPaintProp);
+ }
+ }
+ }
+
+ /** Gets the glow alpha, used by {@link android.animation.ObjectAnimator} via reflection. */
+ public float getGlowAlpha() {
+ return mGlowAlpha;
+ }
+
+ /** Sets the glow alpha, used by {@link android.animation.ObjectAnimator} via reflection. */
+ public void setGlowAlpha(float x) {
+ mGlowAlpha = x;
+ invalidateSelf();
+ }
+
+ /** Gets the glow scale, used by {@link android.animation.ObjectAnimator} via reflection. */
+ public float getGlowScale() {
+ return mGlowScale;
+ }
+
+ /** Sets the glow scale, used by {@link android.animation.ObjectAnimator} via reflection. */
+ public void setGlowScale(float x) {
+ mGlowScale = x;
+ invalidateSelf();
+ }
+
+ private float getMaxGlowAlpha() {
+ return mLastDark ? GLOW_MAX_ALPHA_DARK : GLOW_MAX_ALPHA;
+ }
+
+ @Override
+ protected boolean onStateChange(int[] state) {
+ boolean pressed = false;
+ for (int i = 0; i < state.length; i++) {
+ if (state[i] == android.R.attr.state_pressed) {
+ pressed = true;
+ break;
+ }
+ }
+ if (pressed != mPressed) {
+ setPressed(pressed);
+ mPressed = pressed;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean setVisible(boolean visible, boolean restart) {
+ boolean changed = super.setVisible(visible, restart);
+ if (changed) {
+ // End any existing animations when the visibility changes
+ jumpToCurrentState();
+ }
+ return changed;
+ }
+
+ @Override
+ public void jumpToCurrentState() {
+ endAnimations("jumpToCurrentState", false /* cancel */);
+ }
+
+ @Override
+ public boolean isStateful() {
+ return true;
+ }
+
+ @Override
+ public boolean hasFocusStateSpecified() {
+ return true;
+ }
+
+ public void setPressed(boolean pressed) {
+ if (mDark != mLastDark && pressed) {
+ mRipplePaint = null;
+ mLastDark = mDark;
+ }
+ if (mSupportHardware) {
+ setPressedHardware(pressed);
+ } else {
+ setPressedSoftware(pressed);
+ }
+ }
+
+ /**
+ * Abort the ripple while it is delayed and before shown used only when setShouldDelayStartTouch
+ * is enabled.
+ */
+ public void abortDelayedRipple() {
+ mHandler.removeCallbacksAndMessages(null);
+ }
+
+ private void endAnimations(String reason, boolean cancel) {
+ Trace.beginSection("KeyButtonRipple.endAnim: reason=" + reason + " cancel=" + cancel);
+ Trace.endSection();
+ mVisible = false;
+ mTmpArray.addAll(mRunningAnimations);
+ int size = mTmpArray.size();
+ for (int i = 0; i < size; i++) {
+ Animator a = mTmpArray.get(i);
+ if (cancel) {
+ a.cancel();
+ } else {
+ a.end();
+ }
+ }
+ mTmpArray.clear();
+ mRunningAnimations.clear();
+ mHandler.removeCallbacksAndMessages(null);
+ }
+
+ private void setPressedSoftware(boolean pressed) {
+ if (pressed) {
+ if (mDelayTouchFeedback) {
+ if (mRunningAnimations.isEmpty()) {
+ mHandler.removeCallbacksAndMessages(null);
+ mHandler.postDelayed(this::enterSoftware, ViewConfiguration.getTapTimeout());
+ } else if (mVisible) {
+ enterSoftware();
+ }
+ } else {
+ enterSoftware();
+ }
+ } else {
+ exitSoftware();
+ }
+ }
+
+ private void enterSoftware() {
+ endAnimations("enterSoftware", true /* cancel */);
+ mVisible = true;
+ mGlowAlpha = getMaxGlowAlpha();
+ ObjectAnimator scaleAnimator = ObjectAnimator.ofFloat(this, "glowScale",
+ 0f, GLOW_MAX_SCALE_FACTOR);
+ scaleAnimator.setInterpolator(mInterpolator);
+ scaleAnimator.setDuration(ANIMATION_DURATION_SCALE);
+ scaleAnimator.addListener(mAnimatorListener);
+ scaleAnimator.start();
+ mRunningAnimations.add(scaleAnimator);
+
+ // With the delay, it could eventually animate the enter animation with no pressed state,
+ // then immediately show the exit animation. If this is skipped there will be no ripple.
+ if (mDelayTouchFeedback && !mPressed) {
+ exitSoftware();
+ }
+ }
+
+ private void exitSoftware() {
+ ObjectAnimator alphaAnimator = ObjectAnimator.ofFloat(this, "glowAlpha", mGlowAlpha, 0f);
+ alphaAnimator.setInterpolator(ALPHA_OUT_INTERPOLATOR);
+ alphaAnimator.setDuration(ANIMATION_DURATION_FADE);
+ alphaAnimator.addListener(mAnimatorListener);
+ alphaAnimator.start();
+ mRunningAnimations.add(alphaAnimator);
+ }
+
+ private void setPressedHardware(boolean pressed) {
+ if (pressed) {
+ if (mDelayTouchFeedback) {
+ if (mRunningAnimations.isEmpty()) {
+ mHandler.removeCallbacksAndMessages(null);
+ mHandler.postDelayed(this::enterHardware, ViewConfiguration.getTapTimeout());
+ } else if (mVisible) {
+ enterHardware();
+ }
+ } else {
+ enterHardware();
+ }
+ } else {
+ exitHardware();
+ }
+ }
+
+ /**
+ * Sets the left/top property for the round rect to {@code prop} depending on whether we are
+ * horizontal or vertical mode.
+ */
+ private void setExtendStart(CanvasProperty<Float> prop) {
+ if (isHorizontal()) {
+ mLeftProp = prop;
+ } else {
+ mTopProp = prop;
+ }
+ }
+
+ private CanvasProperty<Float> getExtendStart() {
+ return isHorizontal() ? mLeftProp : mTopProp;
+ }
+
+ /**
+ * Sets the right/bottom property for the round rect to {@code prop} depending on whether we are
+ * horizontal or vertical mode.
+ */
+ private void setExtendEnd(CanvasProperty<Float> prop) {
+ if (isHorizontal()) {
+ mRightProp = prop;
+ } else {
+ mBottomProp = prop;
+ }
+ }
+
+ private CanvasProperty<Float> getExtendEnd() {
+ return isHorizontal() ? mRightProp : mBottomProp;
+ }
+
+ private int getExtendSize() {
+ return isHorizontal() ? getBounds().width() : getBounds().height();
+ }
+
+ private int getRippleSize() {
+ int size = isHorizontal() ? getBounds().width() : getBounds().height();
+ return Math.min(size, mMaxWidth);
+ }
+
+ private void enterHardware() {
+ endAnimations("enterHardware", true /* cancel */);
+ mVisible = true;
+ mDrawingHardwareGlow = true;
+ setExtendStart(CanvasProperty.createFloat(getExtendSize() / 2));
+ final RenderNodeAnimator startAnim = new RenderNodeAnimator(getExtendStart(),
+ getExtendSize()/2 - GLOW_MAX_SCALE_FACTOR * getRippleSize()/2);
+ startAnim.setDuration(ANIMATION_DURATION_SCALE);
+ startAnim.setInterpolator(mInterpolator);
+ startAnim.addListener(mAnimatorListener);
+ startAnim.setTarget(mTargetView);
+
+ setExtendEnd(CanvasProperty.createFloat(getExtendSize() / 2));
+ final RenderNodeAnimator endAnim = new RenderNodeAnimator(getExtendEnd(),
+ getExtendSize()/2 + GLOW_MAX_SCALE_FACTOR * getRippleSize()/2);
+ endAnim.setDuration(ANIMATION_DURATION_SCALE);
+ endAnim.setInterpolator(mInterpolator);
+ endAnim.addListener(mAnimatorListener);
+ endAnim.addListener(mEnterHwTraceAnimator);
+ endAnim.setTarget(mTargetView);
+
+ if (isHorizontal()) {
+ mTopProp = CanvasProperty.createFloat(0f);
+ mBottomProp = CanvasProperty.createFloat(getBounds().height());
+ mRxProp = CanvasProperty.createFloat(getBounds().height()/2);
+ mRyProp = CanvasProperty.createFloat(getBounds().height()/2);
+ } else {
+ mLeftProp = CanvasProperty.createFloat(0f);
+ mRightProp = CanvasProperty.createFloat(getBounds().width());
+ mRxProp = CanvasProperty.createFloat(getBounds().width()/2);
+ mRyProp = CanvasProperty.createFloat(getBounds().width()/2);
+ }
+
+ mGlowScale = GLOW_MAX_SCALE_FACTOR;
+ mGlowAlpha = getMaxGlowAlpha();
+ mRipplePaint = getRipplePaint();
+ mRipplePaint.setAlpha((int) (mGlowAlpha * 255));
+ mPaintProp = CanvasProperty.createPaint(mRipplePaint);
+
+ startAnim.start();
+ endAnim.start();
+ mRunningAnimations.add(startAnim);
+ mRunningAnimations.add(endAnim);
+
+ invalidateSelf();
+
+ // With the delay, it could eventually animate the enter animation with no pressed state,
+ // then immediately show the exit animation. If this is skipped there will be no ripple.
+ if (mDelayTouchFeedback && !mPressed) {
+ exitHardware();
+ }
+ }
+
+ private void exitHardware() {
+ mPaintProp = CanvasProperty.createPaint(getRipplePaint());
+ final RenderNodeAnimator opacityAnim = new RenderNodeAnimator(mPaintProp,
+ RenderNodeAnimator.PAINT_ALPHA, 0);
+ opacityAnim.setDuration(ANIMATION_DURATION_FADE);
+ opacityAnim.setInterpolator(ALPHA_OUT_INTERPOLATOR);
+ opacityAnim.addListener(mAnimatorListener);
+ opacityAnim.addListener(mExitHwTraceAnimator);
+ opacityAnim.setTarget(mTargetView);
+
+ opacityAnim.start();
+ mRunningAnimations.add(opacityAnim);
+
+ invalidateSelf();
+ }
+
+ private final AnimatorListenerAdapter mAnimatorListener =
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mRunningAnimations.remove(animation);
+ if (mRunningAnimations.isEmpty() && !mPressed) {
+ mVisible = false;
+ mDrawingHardwareGlow = false;
+ invalidateSelf();
+ }
+ }
+ };
+
+ private static final class TraceAnimatorListener extends AnimatorListenerAdapter {
+ private final String mName;
+ TraceAnimatorListener(String name) {
+ mName = name;
+ }
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ Trace.beginSection("KeyButtonRipple.start." + mName);
+ Trace.endSection();
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ Trace.beginSection("KeyButtonRipple.cancel." + mName);
+ Trace.endSection();
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ Trace.beginSection("KeyButtonRipple.end." + mName);
+ Trace.endSection();
+ }
+ }
+
+ /**
+ * Interpolator with a smooth log deceleration
+ */
+ private static final class LogInterpolator implements Interpolator {
+ @Override
+ public float getInterpolation(float input) {
+ return 1 - (float) Math.pow(400, -input * 1.4);
+ }
+ }
+}
diff --git a/core/java/android/inputmethodservice/navigationbar/KeyButtonView.java b/core/java/android/inputmethodservice/navigationbar/KeyButtonView.java
new file mode 100644
index 000000000000..74d30f8f8806
--- /dev/null
+++ b/core/java/android/inputmethodservice/navigationbar/KeyButtonView.java
@@ -0,0 +1,370 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.inputmethodservice.navigationbar;
+
+import static android.view.Display.INVALID_DISPLAY;
+import static android.view.KeyEvent.KEYCODE_BACK;
+import static android.view.KeyEvent.KEYCODE_UNKNOWN;
+import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK;
+import static android.view.accessibility.AccessibilityNodeInfo.ACTION_LONG_CLICK;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.drawable.Drawable;
+import android.inputmethodservice.InputMethodService;
+import android.media.AudioManager;
+import android.os.Bundle;
+import android.os.SystemClock;
+import android.util.AttributeSet;
+import android.view.HapticFeedbackConstants;
+import android.view.InputDevice;
+import android.view.KeyCharacterMap;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.SoundEffectConstants;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.inputmethod.InputConnection;
+import android.widget.ImageView;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * @hide
+ */
+public class KeyButtonView extends ImageView implements ButtonInterface {
+ private static final String TAG = KeyButtonView.class.getSimpleName();
+
+ private final boolean mPlaySounds;
+ private long mDownTime;
+ private boolean mTracking;
+ private int mCode;
+ private int mTouchDownX;
+ private int mTouchDownY;
+ private AudioManager mAudioManager;
+ private boolean mGestureAborted;
+ @VisibleForTesting boolean mLongClicked;
+ private OnClickListener mOnClickListener;
+ private final KeyButtonRipple mRipple;
+ private final Paint mOvalBgPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
+ private float mDarkIntensity;
+ private boolean mHasOvalBg = false;
+
+ private final Runnable mCheckLongPress = new Runnable() {
+ public void run() {
+ if (isPressed()) {
+ // Log.d("KeyButtonView", "longpressed: " + this);
+ if (isLongClickable()) {
+ // Just an old-fashioned ImageView
+ performLongClick();
+ mLongClicked = true;
+ } else {
+ if (mCode != KEYCODE_UNKNOWN) {
+ sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.FLAG_LONG_PRESS);
+ sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
+ }
+ mLongClicked = true;
+ }
+ }
+ }
+ };
+
+ public KeyButtonView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ // TODO(b/205803355): Figure out better place to set this.
+ switch (getId()) {
+ case com.android.internal.R.id.input_method_nav_back:
+ mCode = KEYCODE_BACK;
+ break;
+ default:
+ mCode = KEYCODE_UNKNOWN;
+ break;
+ }
+
+ mPlaySounds = true;
+
+ setClickable(true);
+ mAudioManager = context.getSystemService(AudioManager.class);
+
+ mRipple = new KeyButtonRipple(context, this,
+ com.android.internal.R.dimen.input_method_nav_key_button_ripple_max_width);
+ setBackground(mRipple);
+ setWillNotDraw(false);
+ forceHasOverlappingRendering(false);
+ }
+
+ @Override
+ public boolean isClickable() {
+ return mCode != KEYCODE_UNKNOWN || super.isClickable();
+ }
+
+ public void setCode(int code) {
+ mCode = code;
+ }
+
+ @Override
+ public void setOnClickListener(OnClickListener onClickListener) {
+ super.setOnClickListener(onClickListener);
+ mOnClickListener = onClickListener;
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ if (mCode != KEYCODE_UNKNOWN) {
+ info.addAction(new AccessibilityNodeInfo.AccessibilityAction(ACTION_CLICK, null));
+ if (isLongClickable()) {
+ info.addAction(
+ new AccessibilityNodeInfo.AccessibilityAction(ACTION_LONG_CLICK, null));
+ }
+ }
+ }
+
+ @Override
+ protected void onWindowVisibilityChanged(int visibility) {
+ super.onWindowVisibilityChanged(visibility);
+ if (visibility != View.VISIBLE) {
+ jumpDrawablesToCurrentState();
+ }
+ }
+
+ @Override
+ public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
+ if (action == ACTION_CLICK && mCode != KEYCODE_UNKNOWN) {
+ sendEvent(KeyEvent.ACTION_DOWN, 0, SystemClock.uptimeMillis());
+ sendEvent(KeyEvent.ACTION_UP, mTracking ? KeyEvent.FLAG_TRACKING : 0);
+ mTracking = false;
+ sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
+ playSoundEffect(SoundEffectConstants.CLICK);
+ return true;
+ } else if (action == ACTION_LONG_CLICK && mCode != KEYCODE_UNKNOWN) {
+ sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.FLAG_LONG_PRESS);
+ sendEvent(KeyEvent.ACTION_UP, mTracking ? KeyEvent.FLAG_TRACKING : 0);
+ mTracking = false;
+ sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
+ return true;
+ }
+ return super.performAccessibilityActionInternal(action, arguments);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ final boolean showSwipeUI = false; // mOverviewProxyService.shouldShowSwipeUpUI();
+ final int action = ev.getAction();
+ int x, y;
+ if (action == MotionEvent.ACTION_DOWN) {
+ mGestureAborted = false;
+ }
+ if (mGestureAborted) {
+ setPressed(false);
+ return false;
+ }
+
+ switch (action) {
+ case MotionEvent.ACTION_DOWN:
+ mDownTime = SystemClock.uptimeMillis();
+ mLongClicked = false;
+ setPressed(true);
+
+ // Use raw X and Y to detect gestures in case a parent changes the x and y values
+ mTouchDownX = (int) ev.getRawX();
+ mTouchDownY = (int) ev.getRawY();
+ if (mCode != KEYCODE_UNKNOWN) {
+ sendEvent(KeyEvent.ACTION_DOWN, 0, mDownTime);
+ } else {
+ // Provide the same haptic feedback that the system offers for virtual keys.
+ performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
+ }
+ if (!showSwipeUI) {
+ playSoundEffect(SoundEffectConstants.CLICK);
+ }
+ removeCallbacks(mCheckLongPress);
+ postDelayed(mCheckLongPress, ViewConfiguration.getLongPressTimeout());
+ break;
+ case MotionEvent.ACTION_MOVE:
+ x = (int)ev.getRawX();
+ y = (int)ev.getRawY();
+
+ float slop = getQuickStepTouchSlopPx(getContext());
+ if (Math.abs(x - mTouchDownX) > slop || Math.abs(y - mTouchDownY) > slop) {
+ // When quick step is enabled, prevent animating the ripple triggered by
+ // setPressed and decide to run it on touch up
+ setPressed(false);
+ removeCallbacks(mCheckLongPress);
+ }
+ break;
+ case MotionEvent.ACTION_CANCEL:
+ setPressed(false);
+ if (mCode != KEYCODE_UNKNOWN) {
+ sendEvent(KeyEvent.ACTION_UP, KeyEvent.FLAG_CANCELED);
+ }
+ removeCallbacks(mCheckLongPress);
+ break;
+ case MotionEvent.ACTION_UP:
+ final boolean doIt = isPressed() && !mLongClicked;
+ setPressed(false);
+ final boolean doHapticFeedback = (SystemClock.uptimeMillis() - mDownTime) > 150;
+ if (showSwipeUI) {
+ if (doIt) {
+ // Apply haptic feedback on touch up since there is none on touch down
+ performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
+ playSoundEffect(SoundEffectConstants.CLICK);
+ }
+ } else if (doHapticFeedback && !mLongClicked) {
+ // Always send a release ourselves because it doesn't seem to be sent elsewhere
+ // and it feels weird to sometimes get a release haptic and other times not.
+ performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY_RELEASE);
+ }
+ if (mCode != KEYCODE_UNKNOWN) {
+ if (doIt) {
+ sendEvent(KeyEvent.ACTION_UP, mTracking ? KeyEvent.FLAG_TRACKING : 0);
+ mTracking = false;
+ sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
+ } else {
+ sendEvent(KeyEvent.ACTION_UP, KeyEvent.FLAG_CANCELED);
+ }
+ } else {
+ // no key code, just a regular ImageView
+ if (doIt && mOnClickListener != null) {
+ mOnClickListener.onClick(this);
+ sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
+ }
+ }
+ removeCallbacks(mCheckLongPress);
+ break;
+ }
+
+ return true;
+ }
+
+ @Override
+ public void setImageDrawable(Drawable drawable) {
+ super.setImageDrawable(drawable);
+
+ if (drawable == null) {
+ return;
+ }
+ KeyButtonDrawable keyButtonDrawable = (KeyButtonDrawable) drawable;
+ keyButtonDrawable.setDarkIntensity(mDarkIntensity);
+ mHasOvalBg = keyButtonDrawable.hasOvalBg();
+ if (mHasOvalBg) {
+ mOvalBgPaint.setColor(keyButtonDrawable.getDrawableBackgroundColor());
+ }
+ mRipple.setType(keyButtonDrawable.hasOvalBg() ? KeyButtonRipple.Type.OVAL
+ : KeyButtonRipple.Type.ROUNDED_RECT);
+ }
+
+ public void playSoundEffect(int soundConstant) {
+ if (!mPlaySounds) return;
+ mAudioManager.playSoundEffect(soundConstant);
+ }
+
+ public void sendEvent(int action, int flags) {
+ sendEvent(action, flags, SystemClock.uptimeMillis());
+ }
+
+ private void sendEvent(int action, int flags, long when) {
+ if (mCode == KeyEvent.KEYCODE_BACK && flags != KeyEvent.FLAG_LONG_PRESS) {
+ if (action == MotionEvent.ACTION_UP) {
+ // TODO(b/205803355): Implement notifyBackAction();
+ }
+ }
+
+ // TODO(b/205803355): Consolidate this logic to somewhere else.
+ if (mContext instanceof InputMethodService) {
+ final int repeatCount = (flags & KeyEvent.FLAG_LONG_PRESS) != 0 ? 1 : 0;
+ final KeyEvent ev = new KeyEvent(mDownTime, when, action, mCode, repeatCount,
+ 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
+ flags | KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_VIRTUAL_HARD_KEY,
+ InputDevice.SOURCE_KEYBOARD);
+ int displayId = INVALID_DISPLAY;
+
+ // Make KeyEvent work on multi-display environment
+ if (getDisplay() != null) {
+ displayId = getDisplay().getDisplayId();
+ }
+ if (displayId != INVALID_DISPLAY) {
+ ev.setDisplayId(displayId);
+ }
+ final InputMethodService ims = (InputMethodService) mContext;
+ final boolean handled;
+ switch (action) {
+ case KeyEvent.ACTION_DOWN:
+ handled = ims.onKeyDown(ev.getKeyCode(), ev);
+ mTracking = handled && ev.getRepeatCount() == 0 &&
+ (ev.getFlags() & KeyEvent.FLAG_START_TRACKING) != 0;
+ break;
+ case KeyEvent.ACTION_UP:
+ handled = ims.onKeyUp(ev.getKeyCode(), ev);
+ break;
+ default:
+ handled = false;
+ break;
+ }
+ if (!handled) {
+ final InputConnection ic = ims.getCurrentInputConnection();
+ if (ic != null) {
+ ic.sendKeyEvent(ev);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void setDarkIntensity(float darkIntensity) {
+ mDarkIntensity = darkIntensity;
+
+ Drawable drawable = getDrawable();
+ if (drawable != null) {
+ ((KeyButtonDrawable) drawable).setDarkIntensity(darkIntensity);
+ // Since we reuse the same drawable for multiple views, we need to invalidate the view
+ // manually.
+ invalidate();
+ }
+ mRipple.setDarkIntensity(darkIntensity);
+ }
+
+ @Override
+ public void setDelayTouchFeedback(boolean shouldDelay) {
+ mRipple.setDelayTouchFeedback(shouldDelay);
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ if (mHasOvalBg) {
+ int d = Math.min(getWidth(), getHeight());
+ canvas.drawOval(0, 0, d, d, mOvalBgPaint);
+ }
+ super.draw(canvas);
+ }
+
+ /**
+ * Ratio of quickstep touch slop (when system takes over the touch) to view touch slop
+ */
+ public static final float QUICKSTEP_TOUCH_SLOP_RATIO = 3;
+
+ /**
+ * Touch slop for quickstep gesture
+ */
+ private static float getQuickStepTouchSlopPx(Context context) {
+ return QUICKSTEP_TOUCH_SLOP_RATIO * ViewConfiguration.get(context).getScaledTouchSlop();
+ }
+}
diff --git a/core/java/android/inputmethodservice/navigationbar/NavigationBarConstants.java b/core/java/android/inputmethodservice/navigationbar/NavigationBarConstants.java
new file mode 100644
index 000000000000..93c54395f972
--- /dev/null
+++ b/core/java/android/inputmethodservice/navigationbar/NavigationBarConstants.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.inputmethodservice.navigationbar;
+
+import android.annotation.ColorInt;
+
+final class NavigationBarConstants {
+ private NavigationBarConstants() {
+ // Not intended to be instantiated.
+ }
+
+ // Copied from "navbar_back_button_ime_offset"
+ // TODO(b/215443343): Handle this in the drawable then remove this constant.
+ static final float NAVBAR_BACK_BUTTON_IME_OFFSET = 2.0f;
+
+ // Copied from "light_mode_icon_color_single_tone" at packages/SettingsLib/res/values/colors.xml
+ @ColorInt
+ static final int LIGHT_MODE_ICON_COLOR_SINGLE_TONE = 0xffffffff;
+
+ // Copied from "dark_mode_icon_color_single_tone" at packages/SettingsLib/res/values/colors.xml
+ @ColorInt
+ static final int DARK_MODE_ICON_COLOR_SINGLE_TONE = 0x99000000;
+
+ // Copied from "navigation_bar_deadzone_hold"
+ static final int NAVIGATION_BAR_DEADZONE_HOLD = 333;
+
+ // Copied from "navigation_bar_deadzone_hold"
+ static final int NAVIGATION_BAR_DEADZONE_DECAY = 333;
+
+ // Copied from "navigation_bar_deadzone_size"
+ static final float NAVIGATION_BAR_DEADZONE_SIZE = 12.0f;
+
+ // Copied from "navigation_bar_deadzone_size_max"
+ static final float NAVIGATION_BAR_DEADZONE_SIZE_MAX = 32.0f;
+
+ // Copied from "nav_key_button_shadow_offset_x"
+ static final float NAV_KEY_BUTTON_SHADOW_OFFSET_X = 0.0f;
+
+ // Copied from "nav_key_button_shadow_offset_y"
+ static final float NAV_KEY_BUTTON_SHADOW_OFFSET_Y = 1.0f;
+
+ // Copied from "nav_key_button_shadow_radius"
+ static final float NAV_KEY_BUTTON_SHADOW_RADIUS = 0.5f;
+
+ // Copied from "nav_key_button_shadow_color"
+ @ColorInt
+ static final int NAV_KEY_BUTTON_SHADOW_COLOR = 0x30000000;
+}
diff --git a/core/java/android/inputmethodservice/navigationbar/NavigationBarFrame.java b/core/java/android/inputmethodservice/navigationbar/NavigationBarFrame.java
new file mode 100644
index 000000000000..f01173e0fdae
--- /dev/null
+++ b/core/java/android/inputmethodservice/navigationbar/NavigationBarFrame.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.inputmethodservice.navigationbar;
+
+import static android.view.MotionEvent.ACTION_OUTSIDE;
+
+import android.annotation.AttrRes;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.widget.FrameLayout;
+
+/**
+ * @hide
+ */
+public final class NavigationBarFrame extends FrameLayout {
+
+ private DeadZone mDeadZone = null;
+
+ public NavigationBarFrame(@NonNull Context context) {
+ super(context);
+ }
+
+ public NavigationBarFrame(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public NavigationBarFrame(@NonNull Context context, @Nullable AttributeSet attrs,
+ @AttrRes int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ public void setDeadZone(@NonNull DeadZone deadZone) {
+ mDeadZone = deadZone;
+ }
+
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent event) {
+ if (event.getAction() == ACTION_OUTSIDE) {
+ if (mDeadZone != null) {
+ return mDeadZone.onTouchEvent(event);
+ }
+ }
+ return super.dispatchTouchEvent(event);
+ }
+} \ No newline at end of file
diff --git a/core/java/android/inputmethodservice/navigationbar/NavigationBarInflaterView.java b/core/java/android/inputmethodservice/navigationbar/NavigationBarInflaterView.java
new file mode 100644
index 000000000000..d488890b27d4
--- /dev/null
+++ b/core/java/android/inputmethodservice/navigationbar/NavigationBarInflaterView.java
@@ -0,0 +1,429 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.inputmethodservice.navigationbar;
+
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.inputmethodservice.navigationbar.ReverseLinearLayout.ReverseRelativeLayout;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.SparseArray;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.LinearLayout;
+import android.widget.Space;
+
+/**
+ * @hide
+ */
+public final class NavigationBarInflaterView extends FrameLayout {
+
+ private static final String TAG = "NavBarInflater";
+
+ public static final String NAV_BAR_VIEWS = "sysui_nav_bar";
+ public static final String NAV_BAR_LEFT = "sysui_nav_bar_left";
+ public static final String NAV_BAR_RIGHT = "sysui_nav_bar_right";
+
+ public static final String MENU_IME_ROTATE = "menu_ime";
+ public static final String BACK = "back";
+ public static final String HOME = "home";
+ public static final String RECENT = "recent";
+ public static final String NAVSPACE = "space";
+ public static final String CLIPBOARD = "clipboard";
+ public static final String HOME_HANDLE = "home_handle";
+ public static final String KEY = "key";
+ public static final String LEFT = "left";
+ public static final String RIGHT = "right";
+ public static final String CONTEXTUAL = "contextual";
+ public static final String IME_SWITCHER = "ime_switcher";
+
+ public static final String GRAVITY_SEPARATOR = ";";
+ public static final String BUTTON_SEPARATOR = ",";
+
+ public static final String SIZE_MOD_START = "[";
+ public static final String SIZE_MOD_END = "]";
+
+ public static final String KEY_CODE_START = "(";
+ public static final String KEY_IMAGE_DELIM = ":";
+ public static final String KEY_CODE_END = ")";
+ private static final String WEIGHT_SUFFIX = "W";
+ private static final String WEIGHT_CENTERED_SUFFIX = "WC";
+ private static final String ABSOLUTE_SUFFIX = "A";
+ private static final String ABSOLUTE_VERTICAL_CENTERED_SUFFIX = "C";
+
+ // Copied from "config_navBarLayoutHandle:
+ private static final String CONFIG_NAV_BAR_LAYOUT_HANDLE =
+ "back[70AC];home_handle;ime_switcher[70AC]";
+
+ protected LayoutInflater mLayoutInflater;
+ protected LayoutInflater mLandscapeInflater;
+
+ protected FrameLayout mHorizontal;
+
+ SparseArray<ButtonDispatcher> mButtonDispatchers;
+
+ private View mLastPortrait;
+ private View mLastLandscape;
+
+ private boolean mAlternativeOrder;
+
+ public NavigationBarInflaterView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ createInflaters();
+ }
+
+ void createInflaters() {
+ mLayoutInflater = LayoutInflater.from(mContext);
+ Configuration landscape = new Configuration();
+ landscape.setTo(mContext.getResources().getConfiguration());
+ landscape.orientation = Configuration.ORIENTATION_LANDSCAPE;
+ mLandscapeInflater = LayoutInflater.from(mContext.createConfigurationContext(landscape));
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ inflateChildren();
+ clearViews();
+ inflateLayout(getDefaultLayout());
+ }
+
+ private void inflateChildren() {
+ removeAllViews();
+ mHorizontal = (FrameLayout) mLayoutInflater.inflate(
+ com.android.internal.R.layout.input_method_navigation_layout,
+ this /* root */, false /* attachToRoot */);
+ addView(mHorizontal);
+ updateAlternativeOrder();
+ }
+
+ String getDefaultLayout() {
+ return CONFIG_NAV_BAR_LAYOUT_HANDLE;
+ }
+
+ public void setButtonDispatchers(SparseArray<ButtonDispatcher> buttonDispatchers) {
+ mButtonDispatchers = buttonDispatchers;
+ for (int i = 0; i < buttonDispatchers.size(); i++) {
+ initiallyFill(buttonDispatchers.valueAt(i));
+ }
+ }
+
+ void updateButtonDispatchersCurrentView() {
+ if (mButtonDispatchers != null) {
+ View view = mHorizontal;
+ for (int i = 0; i < mButtonDispatchers.size(); i++) {
+ final ButtonDispatcher dispatcher = mButtonDispatchers.valueAt(i);
+ dispatcher.setCurrentView(view);
+ }
+ }
+ }
+
+ void setAlternativeOrder(boolean alternativeOrder) {
+ if (alternativeOrder != mAlternativeOrder) {
+ mAlternativeOrder = alternativeOrder;
+ updateAlternativeOrder();
+ }
+ }
+
+ private void updateAlternativeOrder() {
+ updateAlternativeOrder(mHorizontal.findViewById(
+ com.android.internal.R.id.input_method_nav_ends_group));
+ updateAlternativeOrder(mHorizontal.findViewById(
+ com.android.internal.R.id.input_method_nav_center_group));
+ }
+
+ private void updateAlternativeOrder(View v) {
+ if (v instanceof ReverseLinearLayout) {
+ ((ReverseLinearLayout) v).setAlternativeOrder(mAlternativeOrder);
+ }
+ }
+
+ private void initiallyFill(
+ ButtonDispatcher buttonDispatcher) {
+ addAll(buttonDispatcher, mHorizontal.findViewById(
+ com.android.internal.R.id.input_method_nav_ends_group));
+ addAll(buttonDispatcher, mHorizontal.findViewById(
+ com.android.internal.R.id.input_method_nav_center_group));
+ }
+
+ private void addAll(ButtonDispatcher buttonDispatcher, ViewGroup parent) {
+ for (int i = 0; i < parent.getChildCount(); i++) {
+ // Need to manually search for each id, just in case each group has more than one
+ // of a single id. It probably mostly a waste of time, but shouldn't take long
+ // and will only happen once.
+ if (parent.getChildAt(i).getId() == buttonDispatcher.getId()) {
+ buttonDispatcher.addView(parent.getChildAt(i));
+ }
+ if (parent.getChildAt(i) instanceof ViewGroup) {
+ addAll(buttonDispatcher, (ViewGroup) parent.getChildAt(i));
+ }
+ }
+ }
+
+ protected void inflateLayout(String newLayout) {
+ if (newLayout == null) {
+ newLayout = getDefaultLayout();
+ }
+ String[] sets = newLayout.split(GRAVITY_SEPARATOR, 3);
+ if (sets.length != 3) {
+ Log.d(TAG, "Invalid layout.");
+ newLayout = getDefaultLayout();
+ sets = newLayout.split(GRAVITY_SEPARATOR, 3);
+ }
+ String[] start = sets[0].split(BUTTON_SEPARATOR);
+ String[] center = sets[1].split(BUTTON_SEPARATOR);
+ String[] end = sets[2].split(BUTTON_SEPARATOR);
+ // Inflate these in start to end order or accessibility traversal will be messed up.
+ inflateButtons(start, mHorizontal.findViewById(
+ com.android.internal.R.id.input_method_nav_ends_group),
+ false /* landscape */, true /* start */);
+
+ inflateButtons(center, mHorizontal.findViewById(
+ com.android.internal.R.id.input_method_nav_center_group),
+ false /* landscape */, false /* start */);
+
+ addGravitySpacer(mHorizontal.findViewById(
+ com.android.internal.R.id.input_method_nav_ends_group));
+
+ inflateButtons(end, mHorizontal.findViewById(
+ com.android.internal.R.id.input_method_nav_ends_group),
+ false /* landscape */, false /* start */);
+
+ updateButtonDispatchersCurrentView();
+ }
+
+ private void addGravitySpacer(LinearLayout layout) {
+ layout.addView(new Space(mContext), new LinearLayout.LayoutParams(0, 0, 1));
+ }
+
+ private void inflateButtons(String[] buttons, ViewGroup parent, boolean landscape,
+ boolean start) {
+ for (int i = 0; i < buttons.length; i++) {
+ inflateButton(buttons[i], parent, landscape, start);
+ }
+ }
+
+ private ViewGroup.LayoutParams copy(ViewGroup.LayoutParams layoutParams) {
+ if (layoutParams instanceof LinearLayout.LayoutParams) {
+ return new LinearLayout.LayoutParams(layoutParams.width, layoutParams.height,
+ ((LinearLayout.LayoutParams) layoutParams).weight);
+ }
+ return new LayoutParams(layoutParams.width, layoutParams.height);
+ }
+
+ @Nullable
+ protected View inflateButton(String buttonSpec, ViewGroup parent, boolean landscape,
+ boolean start) {
+ LayoutInflater inflater = landscape ? mLandscapeInflater : mLayoutInflater;
+ View v = createView(buttonSpec, parent, inflater);
+ if (v == null) return null;
+
+ v = applySize(v, buttonSpec, landscape, start);
+ parent.addView(v);
+ addToDispatchers(v);
+ View lastView = landscape ? mLastLandscape : mLastPortrait;
+ View accessibilityView = v;
+ if (v instanceof ReverseRelativeLayout) {
+ accessibilityView = ((ReverseRelativeLayout) v).getChildAt(0);
+ }
+ if (lastView != null) {
+ accessibilityView.setAccessibilityTraversalAfter(lastView.getId());
+ }
+ if (landscape) {
+ mLastLandscape = accessibilityView;
+ } else {
+ mLastPortrait = accessibilityView;
+ }
+ return v;
+ }
+
+ private View applySize(View v, String buttonSpec, boolean landscape, boolean start) {
+ String sizeStr = extractSize(buttonSpec);
+ if (sizeStr == null) return v;
+
+ if (sizeStr.contains(WEIGHT_SUFFIX) || sizeStr.contains(ABSOLUTE_SUFFIX)) {
+ // To support gravity, wrap in RelativeLayout and apply gravity to it.
+ // Children wanting to use gravity must be smaller than the frame.
+ ReverseRelativeLayout frame = new ReverseRelativeLayout(mContext);
+ LayoutParams childParams = new LayoutParams(v.getLayoutParams());
+
+ // Compute gravity to apply
+ int gravity = (landscape) ? (start ? Gravity.TOP : Gravity.BOTTOM)
+ : (start ? Gravity.START : Gravity.END);
+ if (sizeStr.endsWith(WEIGHT_CENTERED_SUFFIX)) {
+ gravity = Gravity.CENTER;
+ } else if (sizeStr.endsWith(ABSOLUTE_VERTICAL_CENTERED_SUFFIX)) {
+ gravity = Gravity.CENTER_VERTICAL;
+ }
+
+ // Set default gravity, flipped if needed in reversed layouts (270 RTL and 90 LTR)
+ frame.setDefaultGravity(gravity);
+ frame.setGravity(gravity); // Apply gravity to root
+
+ frame.addView(v, childParams);
+
+ if (sizeStr.contains(WEIGHT_SUFFIX)) {
+ // Use weighting to set the width of the frame
+ float weight = Float.parseFloat(
+ sizeStr.substring(0, sizeStr.indexOf(WEIGHT_SUFFIX)));
+ frame.setLayoutParams(new LinearLayout.LayoutParams(0, MATCH_PARENT, weight));
+ } else {
+ int width = (int) convertDpToPx(mContext,
+ Float.parseFloat(sizeStr.substring(0, sizeStr.indexOf(ABSOLUTE_SUFFIX))));
+ frame.setLayoutParams(new LinearLayout.LayoutParams(width, MATCH_PARENT));
+ }
+
+ // Ensure ripples can be drawn outside bounds
+ frame.setClipChildren(false);
+ frame.setClipToPadding(false);
+
+ return frame;
+ }
+
+ float size = Float.parseFloat(sizeStr);
+ ViewGroup.LayoutParams params = v.getLayoutParams();
+ params.width = (int) (params.width * size);
+ return v;
+ }
+
+ View createView(String buttonSpec, ViewGroup parent, LayoutInflater inflater) {
+ View v = null;
+ String button = extractButton(buttonSpec);
+ if (LEFT.equals(button)) {
+ button = extractButton(NAVSPACE);
+ } else if (RIGHT.equals(button)) {
+ button = extractButton(MENU_IME_ROTATE);
+ }
+ if (HOME.equals(button)) {
+ //v = inflater.inflate(R.layout.home, parent, false);
+ } else if (BACK.equals(button)) {
+ v = inflater.inflate(com.android.internal.R.layout.input_method_nav_back, parent,
+ false);
+ } else if (RECENT.equals(button)) {
+ //v = inflater.inflate(R.layout.recent_apps, parent, false);
+ } else if (MENU_IME_ROTATE.equals(button)) {
+ //v = inflater.inflate(R.layout.menu_ime, parent, false);
+ } else if (NAVSPACE.equals(button)) {
+ //v = inflater.inflate(R.layout.nav_key_space, parent, false);
+ } else if (CLIPBOARD.equals(button)) {
+ //v = inflater.inflate(R.layout.clipboard, parent, false);
+ } else if (CONTEXTUAL.equals(button)) {
+ //v = inflater.inflate(R.layout.contextual, parent, false);
+ } else if (HOME_HANDLE.equals(button)) {
+ v = inflater.inflate(com.android.internal.R.layout.input_method_nav_home_handle,
+ parent, false);
+ } else if (IME_SWITCHER.equals(button)) {
+ v = inflater.inflate(com.android.internal.R.layout.input_method_nav_ime_switcher,
+ parent, false);
+ } else if (button.startsWith(KEY)) {
+ /*
+ String uri = extractImage(button);
+ int code = extractKeycode(button);
+ v = inflater.inflate(R.layout.custom_key, parent, false);
+ ((KeyButtonView) v).setCode(code);
+ if (uri != null) {
+ if (uri.contains(":")) {
+ ((KeyButtonView) v).loadAsync(Icon.createWithContentUri(uri));
+ } else if (uri.contains("/")) {
+ int index = uri.indexOf('/');
+ String pkg = uri.substring(0, index);
+ int id = Integer.parseInt(uri.substring(index + 1));
+ ((KeyButtonView) v).loadAsync(Icon.createWithResource(pkg, id));
+ }
+ }
+ */
+ }
+ return v;
+ }
+
+ /*
+ public static String extractImage(String buttonSpec) {
+ if (!buttonSpec.contains(KEY_IMAGE_DELIM)) {
+ return null;
+ }
+ final int start = buttonSpec.indexOf(KEY_IMAGE_DELIM);
+ String subStr = buttonSpec.substring(start + 1, buttonSpec.indexOf(KEY_CODE_END));
+ return subStr;
+ }
+
+ public static int extractKeycode(String buttonSpec) {
+ if (!buttonSpec.contains(KEY_CODE_START)) {
+ return 1;
+ }
+ final int start = buttonSpec.indexOf(KEY_CODE_START);
+ String subStr = buttonSpec.substring(start + 1, buttonSpec.indexOf(KEY_IMAGE_DELIM));
+ return Integer.parseInt(subStr);
+ }
+ */
+
+ public static String extractSize(String buttonSpec) {
+ if (!buttonSpec.contains(SIZE_MOD_START)) {
+ return null;
+ }
+ final int sizeStart = buttonSpec.indexOf(SIZE_MOD_START);
+ return buttonSpec.substring(sizeStart + 1, buttonSpec.indexOf(SIZE_MOD_END));
+ }
+
+ public static String extractButton(String buttonSpec) {
+ if (!buttonSpec.contains(SIZE_MOD_START)) {
+ return buttonSpec;
+ }
+ return buttonSpec.substring(0, buttonSpec.indexOf(SIZE_MOD_START));
+ }
+
+ private void addToDispatchers(View v) {
+ if (mButtonDispatchers != null) {
+ final int indexOfKey = mButtonDispatchers.indexOfKey(v.getId());
+ if (indexOfKey >= 0) {
+ mButtonDispatchers.valueAt(indexOfKey).addView(v);
+ }
+ if (v instanceof ViewGroup) {
+ final ViewGroup viewGroup = (ViewGroup)v;
+ final int N = viewGroup.getChildCount();
+ for (int i = 0; i < N; i++) {
+ addToDispatchers(viewGroup.getChildAt(i));
+ }
+ }
+ }
+ }
+
+ private void clearViews() {
+ if (mButtonDispatchers != null) {
+ for (int i = 0; i < mButtonDispatchers.size(); i++) {
+ mButtonDispatchers.valueAt(i).clear();
+ }
+ }
+ clearAllChildren(mHorizontal.findViewById(
+ com.android.internal.R.id.input_method_nav_buttons));
+ }
+
+ private void clearAllChildren(ViewGroup group) {
+ for (int i = 0; i < group.getChildCount(); i++) {
+ ((ViewGroup) group.getChildAt(i)).removeAllViews();
+ }
+ }
+
+ private static float convertDpToPx(Context context, float dp) {
+ return dp * context.getResources().getDisplayMetrics().density;
+ }
+}
diff --git a/core/java/android/inputmethodservice/navigationbar/NavigationBarUtils.java b/core/java/android/inputmethodservice/navigationbar/NavigationBarUtils.java
new file mode 100644
index 000000000000..c6096d7ba0a1
--- /dev/null
+++ b/core/java/android/inputmethodservice/navigationbar/NavigationBarUtils.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.inputmethodservice.navigationbar;
+
+import static android.util.TypedValue.COMPLEX_UNIT_DIP;
+
+import android.content.res.Resources;
+import android.util.TypedValue;
+
+final class NavigationBarUtils {
+ private NavigationBarUtils() {
+ // Not intended to be instantiated.
+ }
+
+ /**
+ * A utility method to convert "dp" to "pixel".
+ *
+ * <p>TODO(b/215443343): Remove this method by migrating DP values from
+ * {@link NavigationBarConstants} to resource files.</p>
+ *
+ * @param dpValue "dp" value to be converted to "pixel"
+ * @param res {@link Resources} to be used when dealing with "dp".
+ * @return the pixels for a given dp value.
+ */
+ static int dpToPx(float dpValue, Resources res) {
+ return (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP, dpValue, res.getDisplayMetrics());
+ }
+}
diff --git a/core/java/android/inputmethodservice/navigationbar/NavigationBarView.java b/core/java/android/inputmethodservice/navigationbar/NavigationBarView.java
new file mode 100644
index 000000000000..42847784dd2b
--- /dev/null
+++ b/core/java/android/inputmethodservice/navigationbar/NavigationBarView.java
@@ -0,0 +1,380 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.inputmethodservice.navigationbar;
+
+import static android.inputmethodservice.navigationbar.NavigationBarConstants.DARK_MODE_ICON_COLOR_SINGLE_TONE;
+import static android.inputmethodservice.navigationbar.NavigationBarConstants.LIGHT_MODE_ICON_COLOR_SINGLE_TONE;
+import static android.inputmethodservice.navigationbar.NavigationBarConstants.NAVBAR_BACK_BUTTON_IME_OFFSET;
+import static android.inputmethodservice.navigationbar.NavigationBarUtils.dpToPx;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
+
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+import android.annotation.DrawableRes;
+import android.annotation.FloatRange;
+import android.app.StatusBarManager;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.Canvas;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.SparseArray;
+import android.view.Display;
+import android.view.MotionEvent;
+import android.view.Surface;
+import android.view.View;
+import android.view.animation.Interpolator;
+import android.view.animation.PathInterpolator;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.FrameLayout;
+
+import java.util.function.Consumer;
+
+/**
+ * @hide
+ */
+public final class NavigationBarView extends FrameLayout {
+ final static boolean DEBUG = false;
+ final static String TAG = "NavBarView";
+
+ // Copied from com.android.systemui.animation.Interpolators#FAST_OUT_SLOW_IN
+ private static final Interpolator FAST_OUT_SLOW_IN = new PathInterpolator(0.4f, 0f, 0.2f, 1f);
+
+ // The current view is always mHorizontal.
+ View mCurrentView = null;
+ private View mHorizontal;
+
+ private int mCurrentRotation = -1;
+
+ int mDisabledFlags = 0;
+ int mNavigationIconHints = StatusBarManager.NAVIGATION_HINT_BACK_ALT;
+ private final int mNavBarMode = NAV_BAR_MODE_GESTURAL;
+
+ private KeyButtonDrawable mBackIcon;
+ private KeyButtonDrawable mImeSwitcherIcon;
+ private Context mLightContext;
+ private final int mLightIconColor;
+ private final int mDarkIconColor;
+
+ private final android.inputmethodservice.navigationbar.DeadZone mDeadZone;
+ private boolean mDeadZoneConsuming = false;
+
+ private final SparseArray<ButtonDispatcher> mButtonDispatchers = new SparseArray<>();
+ private Configuration mConfiguration;
+ private Configuration mTmpLastConfiguration;
+
+ private NavigationBarInflaterView mNavigationInflaterView;
+
+ public NavigationBarView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ mLightContext = context;
+ mLightIconColor = LIGHT_MODE_ICON_COLOR_SINGLE_TONE;
+ mDarkIconColor = DARK_MODE_ICON_COLOR_SINGLE_TONE;
+
+ mConfiguration = new Configuration();
+ mTmpLastConfiguration = new Configuration();
+ mConfiguration.updateFrom(context.getResources().getConfiguration());
+
+ mButtonDispatchers.put(com.android.internal.R.id.input_method_nav_back,
+ new ButtonDispatcher(com.android.internal.R.id.input_method_nav_back));
+ mButtonDispatchers.put(com.android.internal.R.id.input_method_nav_ime_switcher,
+ new ButtonDispatcher(com.android.internal.R.id.input_method_nav_ime_switcher));
+ mButtonDispatchers.put(com.android.internal.R.id.input_method_nav_home_handle,
+ new ButtonDispatcher(com.android.internal.R.id.input_method_nav_home_handle));
+
+ mDeadZone = new android.inputmethodservice.navigationbar.DeadZone(this);
+
+ getImeSwitchButton().setOnClickListener(view -> view.getContext()
+ .getSystemService(InputMethodManager.class).showInputMethodPicker());
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent event) {
+ return shouldDeadZoneConsumeTouchEvents(event) || super.onInterceptTouchEvent(event);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ shouldDeadZoneConsumeTouchEvents(event);
+ return super.onTouchEvent(event);
+ }
+
+ private boolean shouldDeadZoneConsumeTouchEvents(MotionEvent event) {
+ int action = event.getActionMasked();
+ if (action == MotionEvent.ACTION_DOWN) {
+ mDeadZoneConsuming = false;
+ }
+ if (mDeadZone.onTouchEvent(event) || mDeadZoneConsuming) {
+ switch (action) {
+ case MotionEvent.ACTION_DOWN:
+ mDeadZoneConsuming = true;
+ break;
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_UP:
+ mDeadZoneConsuming = false;
+ break;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ public View getCurrentView() {
+ return mCurrentView;
+ }
+
+ /**
+ * Applies {@param consumer} to each of the nav bar views.
+ */
+ public void forEachView(Consumer<View> consumer) {
+ if (mHorizontal != null) {
+ consumer.accept(mHorizontal);
+ }
+ }
+
+ public ButtonDispatcher getBackButton() {
+ return mButtonDispatchers.get(com.android.internal.R.id.input_method_nav_back);
+ }
+
+ public ButtonDispatcher getImeSwitchButton() {
+ return mButtonDispatchers.get(com.android.internal.R.id.input_method_nav_ime_switcher);
+ }
+
+ public ButtonDispatcher getHomeHandle() {
+ return mButtonDispatchers.get(com.android.internal.R.id.input_method_nav_home_handle);
+ }
+
+ public SparseArray<ButtonDispatcher> getButtonDispatchers() {
+ return mButtonDispatchers;
+ }
+
+ private void reloadNavIcons() {
+ updateIcons(Configuration.EMPTY);
+ }
+
+ private void updateIcons(Configuration oldConfig) {
+ final boolean orientationChange = oldConfig.orientation != mConfiguration.orientation;
+ final boolean densityChange = oldConfig.densityDpi != mConfiguration.densityDpi;
+ final boolean dirChange =
+ oldConfig.getLayoutDirection() != mConfiguration.getLayoutDirection();
+
+ if (densityChange || dirChange) {
+ mImeSwitcherIcon = getDrawable(com.android.internal.R.drawable.ic_ime_switcher);
+ }
+ if (orientationChange || densityChange || dirChange) {
+ mBackIcon = getBackDrawable();
+ }
+ }
+
+ public KeyButtonDrawable getBackDrawable() {
+ KeyButtonDrawable drawable = getDrawable(com.android.internal.R.drawable.ic_ime_nav_back);
+ orientBackButton(drawable);
+ return drawable;
+ }
+
+ /**
+ * @return whether this nav bar mode is edge to edge
+ */
+ public static boolean isGesturalMode(int mode) {
+ return mode == NAV_BAR_MODE_GESTURAL;
+ }
+
+ private void orientBackButton(KeyButtonDrawable drawable) {
+ final boolean useAltBack =
+ (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0;
+ final boolean isRtl = mConfiguration.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
+ float degrees = useAltBack ? (isRtl ? 90 : -90) : 0;
+ if (drawable.getRotation() == degrees) {
+ return;
+ }
+
+ if (isGesturalMode(mNavBarMode)) {
+ drawable.setRotation(degrees);
+ return;
+ }
+
+ // Animate the back button's rotation to the new degrees and only in portrait move up the
+ // back button to line up with the other buttons
+ float targetY = useAltBack
+ ? - dpToPx(NAVBAR_BACK_BUTTON_IME_OFFSET, getResources())
+ : 0;
+ ObjectAnimator navBarAnimator = ObjectAnimator.ofPropertyValuesHolder(drawable,
+ PropertyValuesHolder.ofFloat(KeyButtonDrawable.KEY_DRAWABLE_ROTATE, degrees),
+ PropertyValuesHolder.ofFloat(KeyButtonDrawable.KEY_DRAWABLE_TRANSLATE_Y, targetY));
+ navBarAnimator.setInterpolator(FAST_OUT_SLOW_IN);
+ navBarAnimator.setDuration(200);
+ navBarAnimator.start();
+ }
+
+ private KeyButtonDrawable getDrawable(@DrawableRes int icon) {
+ return KeyButtonDrawable.create(mLightContext, mLightIconColor, mDarkIconColor, icon,
+ true /* hasShadow */, null /* ovalBackgroundColor */);
+ }
+
+ @Override
+ public void setLayoutDirection(int layoutDirection) {
+ reloadNavIcons();
+
+ super.setLayoutDirection(layoutDirection);
+ }
+
+ public void setNavigationIconHints(int hints) {
+ if (hints == mNavigationIconHints) return;
+ final boolean newBackAlt = (hints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0;
+ final boolean oldBackAlt =
+ (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0;
+ if (newBackAlt != oldBackAlt) {
+ //onImeVisibilityChanged(newBackAlt);
+ }
+
+ if (DEBUG) {
+ android.widget.Toast.makeText(getContext(), "Navigation icon hints = " + hints, 500)
+ .show();
+ }
+ mNavigationIconHints = hints;
+ updateNavButtonIcons();
+ }
+
+ public void setDisabledFlags(int disabledFlags) {
+ if (mDisabledFlags == disabledFlags) return;
+
+ mDisabledFlags = disabledFlags;
+
+ updateNavButtonIcons();
+ }
+
+ public void updateNavButtonIcons() {
+ // We have to replace or restore the back and home button icons when exiting or entering
+ // carmode, respectively. Recents are not available in CarMode in nav bar so change
+ // to recent icon is not required.
+ KeyButtonDrawable backIcon = mBackIcon;
+ orientBackButton(backIcon);
+ getBackButton().setImageDrawable(backIcon);
+
+ getImeSwitchButton().setImageDrawable(mImeSwitcherIcon);
+
+ // Update IME button visibility, a11y and rotate button always overrides the appearance
+ final boolean imeSwitcherVisible =
+ (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN) != 0;
+ getImeSwitchButton().setVisibility(imeSwitcherVisible ? View.VISIBLE : View.INVISIBLE);
+
+ getBackButton().setVisibility(View.VISIBLE);
+ getHomeHandle().setVisibility(View.INVISIBLE);
+
+ // We used to be reporting the touch regions via notifyActiveTouchRegions() here.
+ // TODO(b/215593010): Consider taking care of this in the Launcher side.
+ }
+
+ private Display getContextDisplay() {
+ return getContext().getDisplay();
+ }
+
+ @Override
+ public void onFinishInflate() {
+ super.onFinishInflate();
+ mNavigationInflaterView = findViewById(com.android.internal.R.id.input_method_nav_inflater);
+ mNavigationInflaterView.setButtonDispatchers(mButtonDispatchers);
+
+ updateOrientationViews();
+ reloadNavIcons();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ mDeadZone.onDraw(canvas);
+ super.onDraw(canvas);
+ }
+
+ private void updateOrientationViews() {
+ mHorizontal = findViewById(com.android.internal.R.id.input_method_nav_horizontal);
+
+ updateCurrentView();
+ }
+
+ private void updateCurrentView() {
+ resetViews();
+ mCurrentView = mHorizontal;
+ mCurrentView.setVisibility(View.VISIBLE);
+ mCurrentRotation = getContextDisplay().getRotation();
+ mNavigationInflaterView.setAlternativeOrder(mCurrentRotation == Surface.ROTATION_90);
+ mNavigationInflaterView.updateButtonDispatchersCurrentView();
+ }
+
+ private void resetViews() {
+ mHorizontal.setVisibility(View.GONE);
+ }
+
+ public void reorient() {
+ updateCurrentView();
+
+ final android.inputmethodservice.navigationbar.NavigationBarFrame frame =
+ getRootView().findViewByPredicate(view -> view instanceof NavigationBarFrame);
+ frame.setDeadZone(mDeadZone);
+ mDeadZone.onConfigurationChanged(mCurrentRotation);
+
+ if (DEBUG) {
+ Log.d(TAG, "reorient(): rot=" + mCurrentRotation);
+ }
+
+ // Resolve layout direction if not resolved since components changing layout direction such
+ // as changing languages will recreate this view and the direction will be resolved later
+ if (!isLayoutDirectionResolved()) {
+ resolveLayoutDirection();
+ }
+ updateNavButtonIcons();
+ }
+
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ mTmpLastConfiguration.updateFrom(mConfiguration);
+ final int changes = mConfiguration.updateFrom(newConfig);
+
+ updateIcons(mTmpLastConfiguration);
+ if (mTmpLastConfiguration.densityDpi != mConfiguration.densityDpi
+ || mTmpLastConfiguration.getLayoutDirection()
+ != mConfiguration.getLayoutDirection()) {
+ // If car mode or density changes, we need to reset the icons.
+ updateNavButtonIcons();
+ }
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ // This needs to happen first as it can changed the enabled state which can affect whether
+ // the back button is visible
+ requestApplyInsets();
+ reorient();
+ updateNavButtonIcons();
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ for (int i = 0; i < mButtonDispatchers.size(); ++i) {
+ mButtonDispatchers.valueAt(i).onDestroy();
+ }
+ }
+
+ public void setDarkIntensity(@FloatRange(from = 0.0f, to = 1.0f) float intensity) {
+ for (int i = 0; i < mButtonDispatchers.size(); ++i) {
+ mButtonDispatchers.valueAt(i).setDarkIntensity(intensity);
+ }
+ }
+}
diff --git a/core/java/android/inputmethodservice/navigationbar/NavigationHandle.java b/core/java/android/inputmethodservice/navigationbar/NavigationHandle.java
new file mode 100644
index 000000000000..273cafb7fca7
--- /dev/null
+++ b/core/java/android/inputmethodservice/navigationbar/NavigationHandle.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.inputmethodservice.navigationbar;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+
+/**
+ * TODO(b/215443343): Remove this file, as IME actually doesn't use this.
+ *
+ * @hide
+ */
+public class NavigationHandle extends View implements ButtonInterface {
+
+ public NavigationHandle(Context context) {
+ this(context, null);
+ }
+
+ public NavigationHandle(Context context, AttributeSet attr) {
+ super(context, attr);
+ setFocusable(false);
+ }
+
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent event) {
+ return false;
+ }
+
+ @Override
+ public void setImageDrawable(Drawable drawable) {
+ }
+
+ @Override
+ public void setDarkIntensity(float intensity) {
+ }
+
+ @Override
+ public void setDelayTouchFeedback(boolean shouldDelay) {
+ }
+}
diff --git a/core/java/android/inputmethodservice/navigationbar/ReverseLinearLayout.java b/core/java/android/inputmethodservice/navigationbar/ReverseLinearLayout.java
new file mode 100644
index 000000000000..68163c35f784
--- /dev/null
+++ b/core/java/android/inputmethodservice/navigationbar/ReverseLinearLayout.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.inputmethodservice.navigationbar;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+import android.widget.RelativeLayout;
+
+import java.util.ArrayList;
+
+/**
+ * Automatically reverses the order of children as they are added.
+ * Also reverse the width and height values of layout params
+ * @hide
+ */
+public class ReverseLinearLayout extends LinearLayout {
+
+ /** If true, the layout is reversed vs. a regular linear layout */
+ private boolean mIsLayoutReverse;
+
+ /** If true, the layout is opposite to it's natural reversity from the layout direction */
+ private boolean mIsAlternativeOrder;
+
+ public ReverseLinearLayout(Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ updateOrder();
+ }
+
+ @Override
+ public void addView(View child) {
+ reverseParams(child.getLayoutParams(), child, mIsLayoutReverse);
+ if (mIsLayoutReverse) {
+ super.addView(child, 0);
+ } else {
+ super.addView(child);
+ }
+ }
+
+ @Override
+ public void addView(View child, ViewGroup.LayoutParams params) {
+ reverseParams(params, child, mIsLayoutReverse);
+ if (mIsLayoutReverse) {
+ super.addView(child, 0, params);
+ } else {
+ super.addView(child, params);
+ }
+ }
+
+ @Override
+ public void onRtlPropertiesChanged(int layoutDirection) {
+ super.onRtlPropertiesChanged(layoutDirection);
+ updateOrder();
+ }
+
+ public void setAlternativeOrder(boolean alternative) {
+ mIsAlternativeOrder = alternative;
+ updateOrder();
+ }
+
+ /**
+ * In landscape, the LinearLayout is not auto mirrored since it is vertical. Therefore we
+ * have to do it manually
+ */
+ private void updateOrder() {
+ boolean isLayoutRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
+ boolean isLayoutReverse = isLayoutRtl ^ mIsAlternativeOrder;
+
+ if (mIsLayoutReverse != isLayoutReverse) {
+ // reversity changed, swap the order of all views.
+ int childCount = getChildCount();
+ ArrayList<View> childList = new ArrayList<>(childCount);
+ for (int i = 0; i < childCount; i++) {
+ childList.add(getChildAt(i));
+ }
+ removeAllViews();
+ for (int i = childCount - 1; i >= 0; i--) {
+ final View child = childList.get(i);
+ super.addView(child);
+ }
+ mIsLayoutReverse = isLayoutReverse;
+ }
+ }
+
+ private static void reverseParams(ViewGroup.LayoutParams params, View child,
+ boolean isLayoutReverse) {
+ if (child instanceof Reversible) {
+ ((Reversible) child).reverse(isLayoutReverse);
+ }
+ if (child.getPaddingLeft() == child.getPaddingRight()
+ && child.getPaddingTop() == child.getPaddingBottom()) {
+ child.setPadding(child.getPaddingTop(), child.getPaddingLeft(),
+ child.getPaddingTop(), child.getPaddingLeft());
+ }
+ if (params == null) {
+ return;
+ }
+ int width = params.width;
+ params.width = params.height;
+ params.height = width;
+ }
+
+ interface Reversible {
+ void reverse(boolean isLayoutReverse);
+ }
+
+ public static class ReverseRelativeLayout extends RelativeLayout implements Reversible {
+
+ public ReverseRelativeLayout(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void reverse(boolean isLayoutReverse) {
+ updateGravity(isLayoutReverse);
+ reverseGroup(this, isLayoutReverse);
+ }
+
+ private int mDefaultGravity = Gravity.NO_GRAVITY;
+ public void setDefaultGravity(int gravity) {
+ mDefaultGravity = gravity;
+ }
+
+ public void updateGravity(boolean isLayoutReverse) {
+ // Flip gravity if top of bottom is used
+ if (mDefaultGravity != Gravity.TOP && mDefaultGravity != Gravity.BOTTOM) return;
+
+ // Use the default (intended for 270 LTR and 90 RTL) unless layout is otherwise
+ int gravityToApply = mDefaultGravity;
+ if (isLayoutReverse) {
+ gravityToApply = mDefaultGravity == Gravity.TOP ? Gravity.BOTTOM : Gravity.TOP;
+ }
+
+ if (getGravity() != gravityToApply) setGravity(gravityToApply);
+ }
+ }
+
+ private static void reverseGroup(ViewGroup group, boolean isLayoutReverse) {
+ for (int i = 0; i < group.getChildCount(); i++) {
+ final View child = group.getChildAt(i);
+ reverseParams(child.getLayoutParams(), child, isLayoutReverse);
+
+ // Recursively reverse all children
+ if (child instanceof ViewGroup) {
+ reverseGroup((ViewGroup) child, isLayoutReverse);
+ }
+ }
+ }
+}
diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl
index 147138e6712a..6284f56c8258 100644
--- a/core/java/android/net/INetworkPolicyManager.aidl
+++ b/core/java/android/net/INetworkPolicyManager.aidl
@@ -70,7 +70,7 @@ interface INetworkPolicyManager {
int getMultipathPreference(in Network network);
SubscriptionPlan getSubscriptionPlan(in NetworkTemplate template);
- void onStatsProviderWarningOrLimitReached();
+ void notifyStatsProviderWarningOrLimitReached();
SubscriptionPlan[] getSubscriptionPlans(int subId, String callingPackage);
void setSubscriptionPlans(int subId, in SubscriptionPlan[] plans, String callingPackage);
String getSubscriptionPlansOwner(int subId);
diff --git a/core/java/android/net/InterfaceConfiguration.java b/core/java/android/net/InterfaceConfiguration.java
index 37425ffc18aa..1c4089c0f366 100644
--- a/core/java/android/net/InterfaceConfiguration.java
+++ b/core/java/android/net/InterfaceConfiguration.java
@@ -160,6 +160,7 @@ public class InterfaceConfiguration implements Parcelable {
}
}
+ @SuppressWarnings("UnsafeParcelApi")
public static final @android.annotation.NonNull Creator<InterfaceConfiguration> CREATOR = new Creator<
InterfaceConfiguration>() {
public InterfaceConfiguration createFromParcel(Parcel in) {
diff --git a/core/java/android/net/NetworkPolicy.java b/core/java/android/net/NetworkPolicy.java
index ab1f5420fb3f..596f4317dce3 100644
--- a/core/java/android/net/NetworkPolicy.java
+++ b/core/java/android/net/NetworkPolicy.java
@@ -142,8 +142,8 @@ public class NetworkPolicy implements Parcelable, Comparable<NetworkPolicy> {
}
private NetworkPolicy(Parcel source) {
- template = source.readParcelable(null);
- cycleRule = source.readParcelable(null);
+ template = source.readParcelable(null, android.net.NetworkTemplate.class);
+ cycleRule = source.readParcelable(null, android.util.RecurrenceRule.class);
warningBytes = source.readLong();
limitBytes = source.readLong();
lastWarningSnooze = source.readLong();
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 426fc617d5fb..c936bfa05118 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -546,7 +546,7 @@ public class NetworkPolicyManager {
@RequiresPermission(anyOf = {
NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
android.Manifest.permission.NETWORK_STACK})
- // @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public SubscriptionPlan getSubscriptionPlan(@NonNull NetworkTemplate template) {
try {
return mService.getSubscriptionPlan(template);
@@ -565,10 +565,10 @@ public class NetworkPolicyManager {
@RequiresPermission(anyOf = {
NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
android.Manifest.permission.NETWORK_STACK})
- // @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
- public void onStatsProviderWarningOrLimitReached() {
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public void notifyStatsProviderWarningOrLimitReached() {
try {
- mService.onStatsProviderWarningOrLimitReached();
+ mService.notifyStatsProviderWarningOrLimitReached();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/net/vcn/VcnConfig.java b/core/java/android/net/vcn/VcnConfig.java
index caab15251f58..fd3fe3731b74 100644
--- a/core/java/android/net/vcn/VcnConfig.java
+++ b/core/java/android/net/vcn/VcnConfig.java
@@ -173,7 +173,7 @@ public final class VcnConfig implements Parcelable {
new Parcelable.Creator<VcnConfig>() {
@NonNull
public VcnConfig createFromParcel(Parcel in) {
- return new VcnConfig((PersistableBundle) in.readParcelable(null));
+ return new VcnConfig((PersistableBundle) in.readParcelable(null, android.os.PersistableBundle.class));
}
@NonNull
diff --git a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
index a6830b708c31..2339656979b5 100644
--- a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
+++ b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
@@ -549,8 +549,8 @@ public final class VcnGatewayConnectionConfig {
* networks, a network will be chosen arbitrarily amongst the networks matching the highest
* priority rule.
*
- * <p>If all networks fail to match the rules provided, an underlying network will still be
- * selected (at random if necessary).
+ * <p>If all networks fail to match the rules provided, a carrier-owned underlying network
+ * will still be selected (if available, at random if necessary).
*
* @param underlyingNetworkTemplates a list of unique VcnUnderlyingNetworkTemplates that are
* ordered from most to least preferred, or an empty list to use the default
diff --git a/core/java/android/net/vcn/VcnNetworkPolicyResult.java b/core/java/android/net/vcn/VcnNetworkPolicyResult.java
index 14e70cfeb18a..fca084a00a79 100644
--- a/core/java/android/net/vcn/VcnNetworkPolicyResult.java
+++ b/core/java/android/net/vcn/VcnNetworkPolicyResult.java
@@ -114,7 +114,7 @@ public final class VcnNetworkPolicyResult implements Parcelable {
public static final @NonNull Creator<VcnNetworkPolicyResult> CREATOR =
new Creator<VcnNetworkPolicyResult>() {
public VcnNetworkPolicyResult createFromParcel(Parcel in) {
- return new VcnNetworkPolicyResult(in.readBoolean(), in.readParcelable(null));
+ return new VcnNetworkPolicyResult(in.readBoolean(), in.readParcelable(null, android.net.NetworkCapabilities.class));
}
public VcnNetworkPolicyResult[] newArray(int size) {
diff --git a/core/java/android/net/vcn/VcnTransportInfo.java b/core/java/android/net/vcn/VcnTransportInfo.java
index 25a257423ce2..5c47b28a7c74 100644
--- a/core/java/android/net/vcn/VcnTransportInfo.java
+++ b/core/java/android/net/vcn/VcnTransportInfo.java
@@ -146,7 +146,7 @@ public class VcnTransportInfo implements TransportInfo, Parcelable {
new Creator<VcnTransportInfo>() {
public VcnTransportInfo createFromParcel(Parcel in) {
final int subId = in.readInt();
- final WifiInfo wifiInfo = in.readParcelable(null);
+ final WifiInfo wifiInfo = in.readParcelable(null, android.net.wifi.WifiInfo.class);
// If all fields are their null values, return null TransportInfo to avoid
// leaking information about this being a VCN Network (instead of macro
diff --git a/core/java/android/net/vcn/VcnUnderlyingNetworkPolicy.java b/core/java/android/net/vcn/VcnUnderlyingNetworkPolicy.java
index b0d4f3be248f..2b5305d05dcd 100644
--- a/core/java/android/net/vcn/VcnUnderlyingNetworkPolicy.java
+++ b/core/java/android/net/vcn/VcnUnderlyingNetworkPolicy.java
@@ -106,7 +106,7 @@ public final class VcnUnderlyingNetworkPolicy implements Parcelable {
public static final @NonNull Creator<VcnUnderlyingNetworkPolicy> CREATOR =
new Creator<VcnUnderlyingNetworkPolicy>() {
public VcnUnderlyingNetworkPolicy createFromParcel(Parcel in) {
- return new VcnUnderlyingNetworkPolicy(in.readParcelable(null));
+ return new VcnUnderlyingNetworkPolicy(in.readParcelable(null, android.net.vcn.VcnNetworkPolicyResult.class));
}
public VcnUnderlyingNetworkPolicy[] newArray(int size) {
diff --git a/core/java/android/nfc/BeamShareData.java b/core/java/android/nfc/BeamShareData.java
index ed3b74ab6308..6a40f98fe21c 100644
--- a/core/java/android/nfc/BeamShareData.java
+++ b/core/java/android/nfc/BeamShareData.java
@@ -47,13 +47,13 @@ public final class BeamShareData implements Parcelable {
@Override
public BeamShareData createFromParcel(Parcel source) {
Uri[] uris = null;
- NdefMessage msg = source.readParcelable(NdefMessage.class.getClassLoader());
+ NdefMessage msg = source.readParcelable(NdefMessage.class.getClassLoader(), android.nfc.NdefMessage.class);
int numUris = source.readInt();
if (numUris > 0) {
uris = new Uri[numUris];
source.readTypedArray(uris, Uri.CREATOR);
}
- UserHandle userHandle = source.readParcelable(UserHandle.class.getClassLoader());
+ UserHandle userHandle = source.readParcelable(UserHandle.class.getClassLoader(), android.os.UserHandle.class);
int flags = source.readInt();
return new BeamShareData(msg, uris, userHandle, flags);
diff --git a/core/java/android/os/BatteryConsumer.java b/core/java/android/os/BatteryConsumer.java
index d2f788f4d0fb..47a272c2337f 100644
--- a/core/java/android/os/BatteryConsumer.java
+++ b/core/java/android/os/BatteryConsumer.java
@@ -192,6 +192,7 @@ public abstract class BatteryConsumer {
POWER_COMPONENT_CPU,
POWER_COMPONENT_MOBILE_RADIO,
POWER_COMPONENT_WIFI,
+ POWER_COMPONENT_BLUETOOTH,
};
static final int COLUMN_INDEX_BATTERY_CONSUMER_TYPE = 0;
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index a4538876c60f..2d338179186e 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -1037,6 +1037,16 @@ public abstract class BatteryStats implements Parcelable {
public abstract long getBluetoothMeasuredBatteryConsumptionUC();
/**
+ * Returns the battery consumption (in microcoulombs) of the uid's bluetooth usage
+ * when in the specified process state.
+ * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
+ *
+ * {@hide}
+ */
+ public abstract long getBluetoothMeasuredBatteryConsumptionUC(
+ @BatteryConsumer.ProcessState int processState);
+
+ /**
* Returns the battery consumption (in microcoulombs) of the uid's cpu usage, derived from
* on device power measurement data.
* Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
@@ -3396,6 +3406,11 @@ public abstract class BatteryStats implements Parcelable {
public abstract WakeLockStats getWakeLockStats();
/**
+ * Returns aggregated Bluetooth stats.
+ */
+ public abstract BluetoothBatteryStats getBluetoothBatteryStats();
+
+ /**
* Returns Timers tracking the total time of each Resource Power Manager state and voter.
*/
public abstract Map<String, ? extends Timer> getRpmStats();
diff --git a/core/java/android/os/BatteryStatsManager.java b/core/java/android/os/BatteryStatsManager.java
index 6339435e539c..2a609b8f6ae4 100644
--- a/core/java/android/os/BatteryStatsManager.java
+++ b/core/java/android/os/BatteryStatsManager.java
@@ -368,6 +368,21 @@ public final class BatteryStatsManager {
}
/**
+ * Retrieves accumulated bluetooth stats.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.BATTERY_STATS)
+ @NonNull
+ public BluetoothBatteryStats getBluetoothBatteryStats() {
+ try {
+ return mBatteryStats.getBluetoothBatteryStats();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Indicates an app acquiring full wifi lock.
*
* @param ws worksource (to be used for battery blaming).
diff --git a/core/java/android/os/BluetoothBatteryStats.aidl b/core/java/android/os/BluetoothBatteryStats.aidl
new file mode 100644
index 000000000000..d0514b67e223
--- /dev/null
+++ b/core/java/android/os/BluetoothBatteryStats.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+/** {@hide} */
+parcelable BluetoothBatteryStats;
diff --git a/core/java/android/os/BluetoothBatteryStats.java b/core/java/android/os/BluetoothBatteryStats.java
new file mode 100644
index 000000000000..3d99a08a59c5
--- /dev/null
+++ b/core/java/android/os/BluetoothBatteryStats.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.annotation.NonNull;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Snapshot of Bluetooth battery stats.
+ *
+ * @hide
+ */
+public class BluetoothBatteryStats implements Parcelable {
+
+ /** @hide */
+ public static class UidStats {
+ public final int uid;
+ public final long scanTimeMs;
+ public final long unoptimizedScanTimeMs;
+ public final int scanResultCount;
+ public final long rxTimeMs;
+ public final long txTimeMs;
+
+ public UidStats(int uid, long scanTimeMs, long unoptimizedScanTimeMs, int scanResultCount,
+ long rxTimeMs, long txTimeMs) {
+ this.uid = uid;
+ this.scanTimeMs = scanTimeMs;
+ this.unoptimizedScanTimeMs = unoptimizedScanTimeMs;
+ this.scanResultCount = scanResultCount;
+ this.rxTimeMs = rxTimeMs;
+ this.txTimeMs = txTimeMs;
+ }
+
+ private UidStats(Parcel in) {
+ uid = in.readInt();
+ scanTimeMs = in.readLong();
+ unoptimizedScanTimeMs = in.readLong();
+ scanResultCount = in.readInt();
+ rxTimeMs = in.readLong();
+ txTimeMs = in.readLong();
+ }
+
+ private void writeToParcel(Parcel out) {
+ out.writeInt(uid);
+ out.writeLong(scanTimeMs);
+ out.writeLong(unoptimizedScanTimeMs);
+ out.writeInt(scanResultCount);
+ out.writeLong(rxTimeMs);
+ out.writeLong(txTimeMs);
+ }
+
+ @Override
+ public String toString() {
+ return "UidStats{"
+ + "uid=" + uid
+ + ", scanTimeMs=" + scanTimeMs
+ + ", unoptimizedScanTimeMs=" + unoptimizedScanTimeMs
+ + ", scanResultCount=" + scanResultCount
+ + ", rxTimeMs=" + rxTimeMs
+ + ", txTimeMs=" + txTimeMs
+ + '}';
+ }
+ }
+
+ private final List<UidStats> mUidStats;
+
+ public BluetoothBatteryStats(@NonNull List<UidStats> uidStats) {
+ mUidStats = uidStats;
+ }
+
+ @NonNull
+ public List<UidStats> getUidStats() {
+ return mUidStats;
+ }
+
+ protected BluetoothBatteryStats(Parcel in) {
+ final int size = in.readInt();
+ mUidStats = new ArrayList<>(size);
+ for (int i = 0; i < size; i++) {
+ mUidStats.add(new UidStats(in));
+ }
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ final int size = mUidStats.size();
+ out.writeInt(size);
+ for (int i = 0; i < size; i++) {
+ UidStats stats = mUidStats.get(i);
+ stats.writeToParcel(out);
+ }
+ }
+
+ public static final Creator<BluetoothBatteryStats> CREATOR =
+ new Creator<BluetoothBatteryStats>() {
+ @Override
+ public BluetoothBatteryStats createFromParcel(Parcel in) {
+ return new BluetoothBatteryStats(in);
+ }
+
+ @Override
+ public BluetoothBatteryStats[] newArray(int size) {
+ return new BluetoothBatteryStats[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+}
diff --git a/core/java/android/os/BluetoothServiceManager.java b/core/java/android/os/BluetoothServiceManager.java
new file mode 100644
index 000000000000..12f7bc84cd9c
--- /dev/null
+++ b/core/java/android/os/BluetoothServiceManager.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.os;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.annotation.SystemApi;
+import android.annotation.SystemApi.Client;
+import android.os.BluetoothServiceManager;
+
+/**
+ * Provides a way to register and obtain the system service binder objects managed by the bluetooth
+ * service.
+ *
+ * @hide
+ */
+@SystemApi(client = Client.MODULE_LIBRARIES)
+public class BluetoothServiceManager {
+
+ /** @hide */
+ public static final String BLUETOOTH_MANAGER_SERVICE = "bluetooth_manager";
+ /**
+ * @hide
+ */
+ public BluetoothServiceManager() {
+ }
+
+ /**
+ * A class that exposes the methods to register and obtain each system service.
+ */
+ public static final class ServiceRegisterer {
+ private final String mServiceName;
+
+ /**
+ * @hide
+ */
+ public ServiceRegisterer(String serviceName) {
+ mServiceName = serviceName;
+ }
+
+ /**
+ * Register a system server binding object for a service.
+ */
+ public void register(@NonNull IBinder service) {
+ ServiceManager.addService(mServiceName, service);
+ }
+
+ /**
+ * Get the system server binding object for a service.
+ *
+ * <p>This blocks until the service instance is ready,
+ * or a timeout happens, in which case it returns null.
+ */
+ @Nullable
+ public IBinder get() {
+ return ServiceManager.getService(mServiceName);
+ }
+
+ /**
+ * Get the system server binding object for a service.
+ *
+ * <p>This blocks until the service instance is ready,
+ * or a timeout happens, in which case it throws {@link ServiceNotFoundException}.
+ */
+ @NonNull
+ public IBinder getOrThrow() throws ServiceNotFoundException {
+ try {
+ return ServiceManager.getServiceOrThrow(mServiceName);
+ } catch (ServiceManager.ServiceNotFoundException e) {
+ throw new ServiceNotFoundException(mServiceName);
+ }
+ }
+
+ /**
+ * Get the system server binding object for a service. If the specified service is
+ * not available, it returns null.
+ */
+ @Nullable
+ public IBinder tryGet() {
+ return ServiceManager.checkService(mServiceName);
+ }
+ }
+
+ /**
+ * See {@link ServiceRegisterer#getOrThrow}.
+ *
+ */
+ public static class ServiceNotFoundException extends ServiceManager.ServiceNotFoundException {
+ /**
+ * Constructor.
+ *
+ * @param name the name of the binder service that cannot be found.
+ *
+ */
+ public ServiceNotFoundException(@NonNull String name) {
+ super(name);
+ }
+ }
+
+ /**
+ * Returns {@link ServiceRegisterer} for the "bluetooth" service.
+ */
+ @NonNull
+ public ServiceRegisterer getBluetoothManagerServiceRegisterer() {
+ return new ServiceRegisterer(BLUETOOTH_MANAGER_SERVICE);
+ }
+}
diff --git a/core/java/android/os/LocaleList.java b/core/java/android/os/LocaleList.java
index cfa823cffe86..b74bb333deae 100644
--- a/core/java/android/os/LocaleList.java
+++ b/core/java/android/os/LocaleList.java
@@ -20,6 +20,7 @@ import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Size;
+import android.annotation.SuppressLint;
import android.compat.annotation.UnsupportedAppUsage;
import android.icu.util.ULocale;
@@ -312,18 +313,30 @@ public final class LocaleList implements Parcelable {
return isPseudoLocale(locale != null ? locale.toLocale() : null);
}
- @IntRange(from=0, to=1)
- private static int matchScore(Locale supported, Locale desired) {
+ /**
+ * Determine whether two locales are considered a match, even if they are not exactly equal.
+ * They are considered as a match when both of their languages and scripts
+ * (explicit or inferred) are identical. This means that a user would be able to understand
+ * the content written in the supported locale even if they say they prefer the desired locale.
+ *
+ * E.g. [zh-HK] matches [zh-Hant]; [en-US] matches [en-CA]
+ *
+ * @param supported The supported {@link Locale} to be compared.
+ * @param desired The desired {@link Locale} to be compared.
+ * @return True if they match, false otherwise.
+ */
+ public static boolean matchesLanguageAndScript(@SuppressLint("UseIcu") @NonNull
+ Locale supported, @SuppressLint("UseIcu") @NonNull Locale desired) {
if (supported.equals(desired)) {
- return 1; // return early so we don't do unnecessary computation
+ return true; // return early so we don't do unnecessary computation
}
if (!supported.getLanguage().equals(desired.getLanguage())) {
- return 0;
+ return false;
}
if (isPseudoLocale(supported) || isPseudoLocale(desired)) {
// The locales are not the same, but the languages are the same, and one of the locales
// is a pseudo-locale. So this is not a match.
- return 0;
+ return false;
}
final String supportedScr = getLikelyScript(supported);
if (supportedScr.isEmpty()) {
@@ -331,20 +344,17 @@ public final class LocaleList implements Parcelable {
// if the locales match. So we fall back to old behavior of matching, which considered
// locales with different regions different.
final String supportedRegion = supported.getCountry();
- return (supportedRegion.isEmpty() ||
- supportedRegion.equals(desired.getCountry()))
- ? 1 : 0;
+ return supportedRegion.isEmpty() || supportedRegion.equals(desired.getCountry());
}
final String desiredScr = getLikelyScript(desired);
// There is no match if the two locales use different scripts. This will most imporantly
// take care of traditional vs simplified Chinese.
- return supportedScr.equals(desiredScr) ? 1 : 0;
+ return supportedScr.equals(desiredScr);
}
private int findFirstMatchIndex(Locale supportedLocale) {
for (int idx = 0; idx < mList.length; idx++) {
- final int score = matchScore(supportedLocale, mList[idx]);
- if (score > 0) {
+ if (matchesLanguageAndScript(supportedLocale, mList[idx])) {
return idx;
}
}
diff --git a/core/java/android/os/Message.java b/core/java/android/os/Message.java
index c62df407ca77..72fb4ae03a63 100644
--- a/core/java/android/os/Message.java
+++ b/core/java/android/os/Message.java
@@ -654,7 +654,7 @@ public final class Message implements Parcelable {
arg1 = source.readInt();
arg2 = source.readInt();
if (source.readInt() != 0) {
- obj = source.readParcelable(getClass().getClassLoader());
+ obj = source.readParcelable(getClass().getClassLoader(), java.lang.Object.class);
}
when = source.readLong();
data = source.readBundle();
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 3bc3ec83bde5..e8b3ae9499fb 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -3711,10 +3711,10 @@ public final class Parcel {
final int m = list.size();
int i = 0;
for (; i < m && i < n; i++) {
- list.set(i, (T) readParcelableInternal(cl, clazz));
+ list.set(i, readParcelableInternal(cl, clazz));
}
for (; i < n; i++) {
- list.add((T) readParcelableInternal(cl, clazz));
+ list.add(readParcelableInternal(cl, clazz));
}
for (; i < m; i++) {
list.remove(n);
@@ -4217,7 +4217,8 @@ public final class Parcel {
* trying to instantiate an element.
*/
@Nullable
- public <T> T readParcelable(@Nullable ClassLoader loader, @NonNull Class<T> clazz) {
+ public <T extends Parcelable> T readParcelable(@Nullable ClassLoader loader,
+ @NonNull Class<? super T> clazz) {
Objects.requireNonNull(clazz);
return readParcelableInternal(loader, clazz);
}
@@ -4227,7 +4228,8 @@ public final class Parcel {
*/
@SuppressWarnings("unchecked")
@Nullable
- private <T> T readParcelableInternal(@Nullable ClassLoader loader, @Nullable Class<T> clazz) {
+ private <T extends Parcelable> T readParcelableInternal(@Nullable ClassLoader loader,
+ @Nullable Class<? super T> clazz) {
Parcelable.Creator<?> creator = readParcelableCreatorInternal(loader, clazz);
if (creator == null) {
return null;
@@ -4463,7 +4465,8 @@ public final class Parcel {
* deserializing the object.
*/
@Nullable
- public <T> T readSerializable(@Nullable ClassLoader loader, @NonNull Class<T> clazz) {
+ public <T extends Serializable> T readSerializable(@Nullable ClassLoader loader,
+ @NonNull Class<? super T> clazz) {
Objects.requireNonNull(clazz);
return readSerializableInternal(
loader == null ? getClass().getClassLoader() : loader, clazz);
@@ -4473,8 +4476,8 @@ public final class Parcel {
* @param clazz The type of the serializable expected or {@code null} for performing no checks
*/
@Nullable
- private <T> T readSerializableInternal(@Nullable final ClassLoader loader,
- @Nullable Class<T> clazz) {
+ private <T extends Serializable> T readSerializableInternal(@Nullable final ClassLoader loader,
+ @Nullable Class<? super T> clazz) {
String name = readString();
if (name == null) {
// For some reason we were unable to read the name of the Serializable (either there
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 742a542efdd1..2fe062268112 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -132,6 +132,7 @@ public class Process {
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@TestApi
+ @SystemApi(client = MODULE_LIBRARIES)
public static final int NFC_UID = 1027;
/**
@@ -279,6 +280,26 @@ public class Process {
public static final int LAST_APPLICATION_UID = 19999;
/**
+ * Defines the start of a range of UIDs going from this number to
+ * {@link #LAST_SUPPLEMENTAL_UID} that are reserved for assigning to
+ * supplemental processes. There is a 1-1 mapping between a supplemental
+ * process UID and the app that it belongs to, which can be computed by
+ * subtracting (FIRST_SUPPLEMENTAL_UID - FIRST_APPLICATION_UID) from the
+ * uid of a supplemental process.
+ *
+ * Note that there are no GIDs associated with these processes; storage
+ * attribution for them will be done using project IDs.
+ * @hide
+ */
+ public static final int FIRST_SUPPLEMENTAL_UID = 20000;
+
+ /**
+ * Last UID that is used for supplemental processes.
+ * @hide
+ */
+ public static final int LAST_SUPPLEMENTAL_UID = 29999;
+
+ /**
* First uid used for fully isolated sandboxed processes spawned from an app zygote
* @hide
*/
@@ -880,6 +901,46 @@ public class Process {
}
/**
+ * Returns whether the provided UID belongs to a supplemental process.
+ *
+ * @hide
+ */
+ @SystemApi(client = MODULE_LIBRARIES)
+ public static final boolean isSupplemental(int uid) {
+ uid = UserHandle.getAppId(uid);
+ return (uid >= FIRST_SUPPLEMENTAL_UID && uid <= LAST_SUPPLEMENTAL_UID);
+ }
+
+ /**
+ *
+ * Returns the app process corresponding to a supplemental process.
+ *
+ * @hide
+ */
+ @SystemApi(client = MODULE_LIBRARIES)
+ public static final int toAppUid(int uid) {
+ return uid - (FIRST_SUPPLEMENTAL_UID - FIRST_APPLICATION_UID);
+ }
+
+ /**
+ *
+ * Returns the supplemental process corresponding to an app process.
+ *
+ * @hide
+ */
+ @SystemApi(client = MODULE_LIBRARIES)
+ public static final int toSupplementalUid(int uid) {
+ return uid + (FIRST_SUPPLEMENTAL_UID - FIRST_APPLICATION_UID);
+ }
+
+ /**
+ * Returns whether the current process is a supplemental process.
+ */
+ public static final boolean isSupplemental() {
+ return isSupplemental(myUid());
+ }
+
+ /**
* Returns the UID assigned to a particular user name, or -1 if there is
* none. If the given string consists of only numbers, it is converted
* directly to a uid.
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index ebbfe47c4417..70aaa5e52c44 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -2993,7 +2993,7 @@ public final class StrictMode {
* should be removed.
*/
public ViolationInfo(Parcel in, boolean unsetGatheringBit) {
- mViolation = (Violation) in.readSerializable();
+ mViolation = (Violation) in.readSerializable(android.os.strictmode.Violation.class.getClassLoader(), android.os.strictmode.Violation.class);
int binderStackSize = in.readInt();
for (int i = 0; i < binderStackSize; i++) {
StackTraceElement[] traceElements = new StackTraceElement[in.readInt()];
diff --git a/core/java/android/os/SystemVibrator.java b/core/java/android/os/SystemVibrator.java
index 003776175f26..0aafaf456a08 100644
--- a/core/java/android/os/SystemVibrator.java
+++ b/core/java/android/os/SystemVibrator.java
@@ -18,18 +18,25 @@ package android.os;
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.util.ArrayMap;
import android.util.Log;
+import android.util.Range;
+import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseBooleanArray;
+import android.util.SparseIntArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Objects;
import java.util.concurrent.Executor;
+import java.util.function.Function;
/**
* Vibrator implementation that controls the main system vibrator.
@@ -51,7 +58,7 @@ public class SystemVibrator extends Vibrator {
private final Object mLock = new Object();
@GuardedBy("mLock")
- private AllVibratorsInfo mVibratorInfo;
+ private VibratorInfo mVibratorInfo;
@UnsupportedAppUsage
public SystemVibrator(Context context) {
@@ -71,6 +78,11 @@ public class SystemVibrator extends Vibrator {
return VibratorInfo.EMPTY_VIBRATOR_INFO;
}
int[] vibratorIds = mVibratorManager.getVibratorIds();
+ if (vibratorIds.length == 0) {
+ // It is known that the device has no vibrator, so cache and return info that
+ // reflects the lack of support for effects/primitives.
+ return mVibratorInfo = new NoVibratorInfo();
+ }
VibratorInfo[] vibratorInfos = new VibratorInfo[vibratorIds.length];
for (int i = 0; i < vibratorIds.length; i++) {
Vibrator vibrator = mVibratorManager.getVibrator(vibratorIds[i]);
@@ -83,7 +95,12 @@ public class SystemVibrator extends Vibrator {
}
vibratorInfos[i] = vibrator.getInfo();
}
- return mVibratorInfo = new AllVibratorsInfo(vibratorInfos);
+ if (vibratorInfos.length == 1) {
+ // Device has a single vibrator info, cache and return successfully loaded info.
+ return mVibratorInfo = new VibratorInfo(/* id= */ -1, vibratorInfos[0]);
+ }
+ // Device has multiple vibrators, generate a single info representing all of them.
+ return mVibratorInfo = new MultiVibratorInfo(vibratorInfos);
}
}
@@ -257,76 +274,281 @@ public class SystemVibrator extends Vibrator {
}
/**
- * Represents all the vibrators information as a single {@link VibratorInfo}.
+ * Represents a device with no vibrator as a single {@link VibratorInfo}.
*
- * <p>This uses the first vibrator on the list as the default one for all hardware spec, but
- * uses an intersection of all vibrators to decide the capabilities and effect/primitive
+ * @hide
+ */
+ @VisibleForTesting
+ public static class NoVibratorInfo extends VibratorInfo {
+ public NoVibratorInfo() {
+ // Use empty arrays to indicate no support, while null would indicate support unknown.
+ super(/* id= */ -1,
+ /* capabilities= */ 0,
+ /* supportedEffects= */ new SparseBooleanArray(),
+ /* supportedBraking= */ new SparseBooleanArray(),
+ /* supportedPrimitives= */ new SparseIntArray(),
+ /* primitiveDelayMax= */ 0,
+ /* compositionSizeMax= */ 0,
+ /* pwlePrimitiveDurationMax= */ 0,
+ /* pwleSizeMax= */ 0,
+ /* qFactor= */ Float.NaN,
+ new FrequencyProfile(/* resonantFrequencyHz= */ Float.NaN,
+ /* minFrequencyHz= */ Float.NaN,
+ /* frequencyResolutionHz= */ Float.NaN,
+ /* maxAmplitudes= */ null));
+ }
+ }
+
+ /**
+ * Represents multiple vibrator information as a single {@link VibratorInfo}.
+ *
+ * <p>This uses an intersection of all vibrators to decide the capabilities and effect/primitive
* support.
*
* @hide
*/
@VisibleForTesting
- public static class AllVibratorsInfo extends VibratorInfo {
- private final VibratorInfo[] mVibratorInfos;
+ public static class MultiVibratorInfo extends VibratorInfo {
+ // Epsilon used for float comparison applied in calculations for the merged info.
+ private static final float EPSILON = 1e-5f;
+
+ public MultiVibratorInfo(VibratorInfo[] vibrators) {
+ super(/* id= */ -1,
+ capabilitiesIntersection(vibrators),
+ supportedEffectsIntersection(vibrators),
+ supportedBrakingIntersection(vibrators),
+ supportedPrimitivesAndDurationsIntersection(vibrators),
+ integerLimitIntersection(vibrators, VibratorInfo::getPrimitiveDelayMax),
+ integerLimitIntersection(vibrators, VibratorInfo::getCompositionSizeMax),
+ integerLimitIntersection(vibrators, VibratorInfo::getPwlePrimitiveDurationMax),
+ integerLimitIntersection(vibrators, VibratorInfo::getPwleSizeMax),
+ floatPropertyIntersection(vibrators, VibratorInfo::getQFactor),
+ frequencyProfileIntersection(vibrators));
+ }
- public AllVibratorsInfo(VibratorInfo[] vibrators) {
- super(/* id= */ -1, capabilitiesIntersection(vibrators),
- vibrators.length > 0 ? vibrators[0] : VibratorInfo.EMPTY_VIBRATOR_INFO);
- mVibratorInfos = vibrators;
+ private static int capabilitiesIntersection(VibratorInfo[] infos) {
+ int intersection = ~0;
+ for (VibratorInfo info : infos) {
+ intersection &= info.getCapabilities();
+ }
+ return intersection;
}
- @Override
- public int isEffectSupported(int effectId) {
- if (mVibratorInfos.length == 0) {
- return Vibrator.VIBRATION_EFFECT_SUPPORT_NO;
+ @Nullable
+ private static SparseBooleanArray supportedBrakingIntersection(VibratorInfo[] infos) {
+ for (VibratorInfo info : infos) {
+ if (!info.isBrakingSupportKnown()) {
+ // If one vibrator support is unknown, then the intersection is also unknown.
+ return null;
+ }
}
- int supported = Vibrator.VIBRATION_EFFECT_SUPPORT_YES;
- for (VibratorInfo info : mVibratorInfos) {
- int effectSupported = info.isEffectSupported(effectId);
- if (effectSupported == Vibrator.VIBRATION_EFFECT_SUPPORT_NO) {
- return effectSupported;
- } else if (effectSupported == Vibrator.VIBRATION_EFFECT_SUPPORT_UNKNOWN) {
- supported = effectSupported;
+
+ SparseBooleanArray intersection = new SparseBooleanArray();
+ SparseBooleanArray firstVibratorBraking = infos[0].getSupportedBraking();
+
+ brakingIdLoop:
+ for (int i = 0; i < firstVibratorBraking.size(); i++) {
+ int brakingId = firstVibratorBraking.keyAt(i);
+ if (!firstVibratorBraking.valueAt(i)) {
+ // The first vibrator already doesn't support this braking, so skip it.
+ continue brakingIdLoop;
+ }
+
+ for (int j = 1; j < infos.length; j++) {
+ if (!infos[j].hasBrakingSupport(brakingId)) {
+ // One vibrator doesn't support this braking, so the intersection doesn't.
+ continue brakingIdLoop;
+ }
}
+
+ intersection.put(brakingId, true);
}
- return supported;
+
+ return intersection;
}
- @Override
- public boolean isPrimitiveSupported(int primitiveId) {
- if (mVibratorInfos.length == 0) {
- return false;
+ @Nullable
+ private static SparseBooleanArray supportedEffectsIntersection(VibratorInfo[] infos) {
+ for (VibratorInfo info : infos) {
+ if (!info.isEffectSupportKnown()) {
+ // If one vibrator support is unknown, then the intersection is also unknown.
+ return null;
+ }
}
- for (VibratorInfo info : mVibratorInfos) {
- if (!info.isPrimitiveSupported(primitiveId)) {
- return false;
+
+ SparseBooleanArray intersection = new SparseBooleanArray();
+ SparseBooleanArray firstVibratorEffects = infos[0].getSupportedEffects();
+
+ effectIdLoop:
+ for (int i = 0; i < firstVibratorEffects.size(); i++) {
+ int effectId = firstVibratorEffects.keyAt(i);
+ if (!firstVibratorEffects.valueAt(i)) {
+ // The first vibrator already doesn't support this effect, so skip it.
+ continue effectIdLoop;
}
+
+ for (int j = 1; j < infos.length; j++) {
+ if (infos[j].isEffectSupported(effectId) != VIBRATION_EFFECT_SUPPORT_YES) {
+ // One vibrator doesn't support this effect, so the intersection doesn't.
+ continue effectIdLoop;
+ }
+ }
+
+ intersection.put(effectId, true);
}
- return true;
+
+ return intersection;
}
- @Override
- public int getPrimitiveDuration(int primitiveId) {
- int maxDuration = 0;
- for (VibratorInfo info : mVibratorInfos) {
- int duration = info.getPrimitiveDuration(primitiveId);
- if (duration == 0) {
- return 0;
+ @NonNull
+ private static SparseIntArray supportedPrimitivesAndDurationsIntersection(
+ VibratorInfo[] infos) {
+ SparseIntArray intersection = new SparseIntArray();
+ SparseIntArray firstVibratorPrimitives = infos[0].getSupportedPrimitives();
+
+ primitiveIdLoop:
+ for (int i = 0; i < firstVibratorPrimitives.size(); i++) {
+ int primitiveId = firstVibratorPrimitives.keyAt(i);
+ int primitiveDuration = firstVibratorPrimitives.valueAt(i);
+ if (primitiveDuration == 0) {
+ // The first vibrator already doesn't support this primitive, so skip it.
+ continue primitiveIdLoop;
}
- maxDuration = Math.max(maxDuration, duration);
+
+ for (int j = 1; j < infos.length; j++) {
+ int vibratorPrimitiveDuration = infos[j].getPrimitiveDuration(primitiveId);
+ if (vibratorPrimitiveDuration == 0) {
+ // One vibrator doesn't support this primitive, so the intersection doesn't.
+ continue primitiveIdLoop;
+ } else {
+ // The primitive vibration duration is the maximum among all vibrators.
+ primitiveDuration = Math.max(primitiveDuration, vibratorPrimitiveDuration);
+ }
+ }
+
+ intersection.put(primitiveId, primitiveDuration);
}
- return maxDuration;
+ return intersection;
}
- private static int capabilitiesIntersection(VibratorInfo[] infos) {
- if (infos.length == 0) {
- return 0;
+ private static int integerLimitIntersection(VibratorInfo[] infos,
+ Function<VibratorInfo, Integer> propertyGetter) {
+ int limit = 0; // Limit 0 means unlimited
+ for (VibratorInfo info : infos) {
+ int vibratorLimit = propertyGetter.apply(info);
+ if ((limit == 0) || (vibratorLimit > 0 && vibratorLimit < limit)) {
+ // This vibrator is limited and intersection is unlimited or has a larger limit:
+ // use smaller limit here for the intersection.
+ limit = vibratorLimit;
+ }
}
- int intersection = ~0;
+ return limit;
+ }
+
+ private static float floatPropertyIntersection(VibratorInfo[] infos,
+ Function<VibratorInfo, Float> propertyGetter) {
+ float property = propertyGetter.apply(infos[0]);
+ if (Float.isNaN(property)) {
+ // If one vibrator is undefined then the intersection is undefined.
+ return Float.NaN;
+ }
+ for (int i = 1; i < infos.length; i++) {
+ if (Float.compare(property, propertyGetter.apply(infos[i])) != 0) {
+ // If one vibrator has a different value then the intersection is undefined.
+ return Float.NaN;
+ }
+ }
+ return property;
+ }
+
+ @NonNull
+ private static FrequencyProfile frequencyProfileIntersection(VibratorInfo[] infos) {
+ float freqResolution = floatPropertyIntersection(infos,
+ info -> info.getFrequencyProfile().getFrequencyResolutionHz());
+ float resonantFreq = floatPropertyIntersection(infos,
+ VibratorInfo::getResonantFrequencyHz);
+ Range<Float> freqRange = frequencyRangeIntersection(infos, freqResolution);
+
+ if ((freqRange == null) || Float.isNaN(freqResolution)) {
+ return new FrequencyProfile(resonantFreq, Float.NaN, freqResolution, null);
+ }
+
+ int amplitudeCount =
+ Math.round(1 + (freqRange.getUpper() - freqRange.getLower()) / freqResolution);
+ float[] maxAmplitudes = new float[amplitudeCount];
+
+ // Use MAX_VALUE here to ensure that the FrequencyProfile constructor called with this
+ // will fail if the loop below is broken and do not replace filled values with actual
+ // vibrator measurements.
+ Arrays.fill(maxAmplitudes, Float.MAX_VALUE);
+
for (VibratorInfo info : infos) {
- intersection &= info.getCapabilities();
+ Range<Float> vibratorFreqRange = info.getFrequencyProfile().getFrequencyRangeHz();
+ float[] vibratorMaxAmplitudes = info.getFrequencyProfile().getMaxAmplitudes();
+ int vibratorStartIdx = Math.round(
+ (freqRange.getLower() - vibratorFreqRange.getLower()) / freqResolution);
+ int vibratorEndIdx = vibratorStartIdx + maxAmplitudes.length - 1;
+
+ if ((vibratorStartIdx < 0) || (vibratorEndIdx >= vibratorMaxAmplitudes.length)) {
+ Slog.w(TAG, "Error calculating the intersection of vibrator frequency"
+ + " profiles: attempted to fetch from vibrator "
+ + info.getId() + " max amplitude with bad index " + vibratorStartIdx);
+ return new FrequencyProfile(resonantFreq, Float.NaN, Float.NaN, null);
+ }
+
+ for (int i = 0; i < maxAmplitudes.length; i++) {
+ maxAmplitudes[i] = Math.min(maxAmplitudes[i],
+ vibratorMaxAmplitudes[vibratorStartIdx + i]);
+ }
}
- return intersection;
+
+ return new FrequencyProfile(resonantFreq, freqRange.getLower(),
+ freqResolution, maxAmplitudes);
+ }
+
+ @Nullable
+ private static Range<Float> frequencyRangeIntersection(VibratorInfo[] infos,
+ float frequencyResolution) {
+ Range<Float> firstRange = infos[0].getFrequencyProfile().getFrequencyRangeHz();
+ if (firstRange == null) {
+ // If one vibrator is undefined then the intersection is undefined.
+ return null;
+ }
+ float intersectionLower = firstRange.getLower();
+ float intersectionUpper = firstRange.getUpper();
+
+ // Generate the intersection of all vibrator supported ranges, making sure that both
+ // min supported frequencies are aligned w.r.t. the frequency resolution.
+
+ for (int i = 1; i < infos.length; i++) {
+ Range<Float> vibratorRange = infos[i].getFrequencyProfile().getFrequencyRangeHz();
+ if (vibratorRange == null) {
+ // If one vibrator is undefined then the intersection is undefined.
+ return null;
+ }
+
+ if ((vibratorRange.getLower() >= intersectionUpper)
+ || (vibratorRange.getUpper() <= intersectionLower)) {
+ // If the range and intersection are disjoint then the intersection is undefined
+ return null;
+ }
+
+ float frequencyDelta = Math.abs(intersectionLower - vibratorRange.getLower());
+ if ((frequencyDelta % frequencyResolution) > EPSILON) {
+ // If the intersection is not aligned with one vibrator then it's undefined
+ return null;
+ }
+
+ intersectionLower = Math.max(intersectionLower, vibratorRange.getLower());
+ intersectionUpper = Math.min(intersectionUpper, vibratorRange.getUpper());
+ }
+
+ if ((intersectionUpper - intersectionLower) < frequencyResolution) {
+ // If the intersection is empty then it's undefined.
+ return null;
+ }
+
+ return Range.create(intersectionLower, intersectionUpper);
}
}
diff --git a/core/java/android/os/TEST_MAPPING b/core/java/android/os/TEST_MAPPING
index 07f40828eb56..22ddbccbaaae 100644
--- a/core/java/android/os/TEST_MAPPING
+++ b/core/java/android/os/TEST_MAPPING
@@ -31,15 +31,6 @@
]
},
{
- "file_patterns": ["Environment\\.java"],
- "name": "FrameworksServicesTests",
- "options": [
- {
- "include-filter": "com.android.server.pm.parsing.PackageInfoUserFieldsTest"
- }
- ]
- },
- {
"file_patterns": [
"BatteryStats[^/]*\\.java",
"BatteryUsageStats[^/]*\\.java",
diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java
index d974e0c0713a..6b869f13059d 100644
--- a/core/java/android/os/Trace.java
+++ b/core/java/android/os/Trace.java
@@ -16,7 +16,10 @@
package android.os;
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
import android.annotation.NonNull;
+import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
import dalvik.annotation.optimization.CriticalNative;
@@ -90,6 +93,7 @@ public final class Trace {
/** @hide */
public static final long TRACE_TAG_DATABASE = 1L << 20;
/** @hide */
+ @SystemApi(client = MODULE_LIBRARIES)
public static final long TRACE_TAG_NETWORK = 1L << 21;
/** @hide */
public static final long TRACE_TAG_ADB = 1L << 22;
@@ -148,6 +152,7 @@ public final class Trace {
* @hide
*/
@UnsupportedAppUsage
+ @SystemApi(client = MODULE_LIBRARIES)
public static boolean isTagEnabled(long traceTag) {
long tags = nativeGetEnabledTags();
return (tags & traceTag) != 0;
@@ -163,7 +168,8 @@ public final class Trace {
* @hide
*/
@UnsupportedAppUsage
- public static void traceCounter(long traceTag, String counterName, int counterValue) {
+ @SystemApi(client = MODULE_LIBRARIES)
+ public static void traceCounter(long traceTag, @NonNull String counterName, int counterValue) {
if (isTagEnabled(traceTag)) {
nativeTraceCounter(traceTag, counterName, counterValue);
}
@@ -202,7 +208,8 @@ public final class Trace {
* @hide
*/
@UnsupportedAppUsage
- public static void traceBegin(long traceTag, String methodName) {
+ @SystemApi(client = MODULE_LIBRARIES)
+ public static void traceBegin(long traceTag, @NonNull String methodName) {
if (isTagEnabled(traceTag)) {
nativeTraceBegin(traceTag, methodName);
}
@@ -217,6 +224,7 @@ public final class Trace {
* @hide
*/
@UnsupportedAppUsage
+ @SystemApi(client = MODULE_LIBRARIES)
public static void traceEnd(long traceTag) {
if (isTagEnabled(traceTag)) {
nativeTraceEnd(traceTag);
@@ -237,7 +245,8 @@ public final class Trace {
* @hide
*/
@UnsupportedAppUsage
- public static void asyncTraceBegin(long traceTag, String methodName, int cookie) {
+ @SystemApi(client = MODULE_LIBRARIES)
+ public static void asyncTraceBegin(long traceTag, @NonNull String methodName, int cookie) {
if (isTagEnabled(traceTag)) {
nativeAsyncTraceBegin(traceTag, methodName, cookie);
}
@@ -255,7 +264,8 @@ public final class Trace {
* @hide
*/
@UnsupportedAppUsage
- public static void asyncTraceEnd(long traceTag, String methodName, int cookie) {
+ @SystemApi(client = MODULE_LIBRARIES)
+ public static void asyncTraceEnd(long traceTag, @NonNull String methodName, int cookie) {
if (isTagEnabled(traceTag)) {
nativeAsyncTraceEnd(traceTag, methodName, cookie);
}
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 190f5f127f3d..f18c9c92889e 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -16,6 +16,9 @@
package android.os;
+import static android.app.admin.DevicePolicyResources.Strings.Core.WORK_PROFILE_BADGED_LABEL;
+import static android.app.admin.DevicePolicyResources.Strings.UNDEFINED;
+
import android.Manifest;
import android.accounts.AccountManager;
import android.annotation.ColorInt;
@@ -820,6 +823,20 @@ public class UserManager {
public static final String DISALLOW_ADD_MANAGED_PROFILE = "no_add_managed_profile";
/**
+ * Specifies if a user is disallowed from creating clone profile.
+ * <p>The default value for an unmanaged user is <code>false</code>.
+ * For users with a device owner set, the default is <code>true</code>.
+ *
+ * <p>Key for user restrictions.
+ * <p>Type: Boolean
+ * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+ * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+ * @see #getUserRestrictions()
+ * @hide
+ */
+ public static final String DISALLOW_ADD_CLONE_PROFILE = "no_add_clone_profile";
+
+ /**
* Specifies if a user is disallowed from disabling application verification. The default
* value is <code>false</code>.
*
@@ -1497,6 +1514,7 @@ public class UserManager {
DISALLOW_FACTORY_RESET,
DISALLOW_ADD_USER,
DISALLOW_ADD_MANAGED_PROFILE,
+ DISALLOW_ADD_CLONE_PROFILE,
ENSURE_VERIFY_APPS,
DISALLOW_CONFIG_CELL_BROADCASTS,
DISALLOW_CONFIG_MOBILE_NETWORKS,
@@ -4645,6 +4663,18 @@ public class UserManager {
if (!hasBadge(userId)) {
return label;
}
+ DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
+ return dpm.getString(
+ getUpdatableUserBadgedLabelId(userId),
+ () -> getDefaultUserBadgedLabel(label, userId),
+ /* formatArgs= */ label);
+ }
+
+ private String getUpdatableUserBadgedLabelId(int userId) {
+ return isManagedProfile(userId) ? WORK_PROFILE_BADGED_LABEL : UNDEFINED;
+ }
+
+ private String getDefaultUserBadgedLabel(CharSequence label, int userId) {
try {
final int resourceId = mService.getUserBadgeLabelResId(userId);
return Resources.getSystem().getString(resourceId, label);
diff --git a/core/java/android/os/VibrationAttributes.java b/core/java/android/os/VibrationAttributes.java
index 8834725cc3e9..8bc219b7dc57 100644
--- a/core/java/android/os/VibrationAttributes.java
+++ b/core/java/android/os/VibrationAttributes.java
@@ -136,6 +136,7 @@ public final class VibrationAttributes implements Parcelable {
*/
@IntDef(prefix = { "FLAG_" }, flag = true, value = {
FLAG_BYPASS_INTERRUPTION_POLICY,
+ FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF
})
@Retention(RetentionPolicy.SOURCE)
public @interface Flag{}
@@ -146,10 +147,22 @@ public final class VibrationAttributes implements Parcelable {
public static final int FLAG_BYPASS_INTERRUPTION_POLICY = 0x1;
/**
+ * Flag requesting vibration effect to be played even when user settings are disabling it.
+ *
+ * <p>Flag introduced to represent
+ * {@link android.view.HapticFeedbackConstants#FLAG_IGNORE_GLOBAL_SETTING} and
+ * {@link AudioAttributes#FLAG_BYPASS_MUTE}.
+ *
+ * @hide
+ */
+ public static final int FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF = 0x2;
+
+ /**
* All flags supported by vibrator service, update it when adding new flag.
* @hide
*/
- public static final int FLAG_ALL_SUPPORTED = FLAG_BYPASS_INTERRUPTION_POLICY;
+ public static final int FLAG_ALL_SUPPORTED =
+ FLAG_BYPASS_INTERRUPTION_POLICY | FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF;
/** Creates a new {@link VibrationAttributes} instance with given usage. */
public static @NonNull VibrationAttributes createForUsage(int usage) {
@@ -397,6 +410,11 @@ public final class VibrationAttributes implements Parcelable {
if ((audio.getAllFlags() & AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY) != 0) {
mFlags |= FLAG_BYPASS_INTERRUPTION_POLICY;
}
+ if ((audio.getAllFlags() & AudioAttributes.FLAG_BYPASS_MUTE) != 0) {
+ // Muted audio stream translates to vibration usage having the value
+ // Vibrator.VIBRATION_INTENSITY_OFF set in the user setting.
+ mFlags |= FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF;
+ }
}
/**
diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java
index 5de455695c01..ae37a714e0c8 100644
--- a/core/java/android/os/VibrationEffect.java
+++ b/core/java/android/os/VibrationEffect.java
@@ -576,7 +576,7 @@ public abstract class VibrationEffect implements Parcelable {
private final int mRepeatIndex;
Composed(@NonNull Parcel in) {
- this(in.readArrayList(VibrationEffectSegment.class.getClassLoader()), in.readInt());
+ this(in.readArrayList(VibrationEffectSegment.class.getClassLoader(), android.os.vibrator.VibrationEffectSegment.class), in.readInt());
}
Composed(@NonNull VibrationEffectSegment segment) {
diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java
index eba9ff1bb4f8..23baa5d70c66 100644
--- a/core/java/android/os/Vibrator.java
+++ b/core/java/android/os/Vibrator.java
@@ -31,6 +31,7 @@ import android.content.res.Resources;
import android.hardware.vibrator.IVibrator;
import android.media.AudioAttributes;
import android.os.vibrator.VibrationConfig;
+import android.os.vibrator.VibratorFrequencyProfile;
import android.util.Log;
import java.lang.annotation.Retention;
@@ -208,8 +209,8 @@ public abstract class Vibrator {
/**
* Check whether the vibrator has independent frequency control.
*
- * @return True if the hardware can control the frequency of the vibrations, otherwise false.
- * @hide
+ * @return True if the hardware can control the frequency of the vibrations independently of
+ * the vibration amplitude, false otherwise.
*/
public boolean hasFrequencyControl() {
// We currently can only control frequency of the vibration using the compose PWLE method.
@@ -229,28 +230,48 @@ public abstract class Vibrator {
}
/**
- * Gets the resonant frequency of the vibrator.
+ * Gets the resonant frequency of the vibrator, if applicable.
*
- * @return the resonant frequency of the vibrator, or {@link Float#NaN NaN} if it's unknown or
- * this vibrator is a composite of multiple physical devices.
- * @hide
+ * @return the resonant frequency of the vibrator, or {@link Float#NaN NaN} if it's unknown, not
+ * applicable, or if this vibrator is a composite of multiple physical devices with different
+ * frequencies.
*/
public float getResonantFrequency() {
- return getInfo().getResonantFrequency();
+ return getInfo().getResonantFrequencyHz();
}
/**
* Gets the <a href="https://en.wikipedia.org/wiki/Q_factor">Q factor</a> of the vibrator.
*
- * @return the Q factor of the vibrator, or {@link Float#NaN NaN} if it's unknown or
- * this vibrator is a composite of multiple physical devices.
- * @hide
+ * @return the Q factor of the vibrator, or {@link Float#NaN NaN} if it's unknown, not
+ * applicable, or if this vibrator is a composite of multiple physical devices with different
+ * Q factors.
*/
public float getQFactor() {
return getInfo().getQFactor();
}
/**
+ * Gets the profile that describes the vibrator output across the supported frequency range.
+ *
+ * <p>The profile describes the relative output acceleration that the device can reach when it
+ * vibrates at different frequencies.
+ *
+ * @return The frequency profile for this vibrator, or null if the vibrator does not have
+ * frequency control. If this vibrator is a composite of multiple physical devices then this
+ * will return a profile supported in all devices, or null if the intersection is empty or not
+ * available.
+ */
+ @Nullable
+ public VibratorFrequencyProfile getFrequencyProfile() {
+ VibratorInfo.FrequencyProfile frequencyProfile = getInfo().getFrequencyProfile();
+ if (frequencyProfile.isEmpty()) {
+ return null;
+ }
+ return new VibratorFrequencyProfile(frequencyProfile);
+ }
+
+ /**
* Return the maximum amplitude the vibrator can play using the audio haptic channels.
*
* <p>This is a positive value, or {@link Float#NaN NaN} if it's unknown. If this returns a
diff --git a/core/java/android/os/VibratorInfo.java b/core/java/android/os/VibratorInfo.java
index 189e454f1488..00ce14fdfd28 100644
--- a/core/java/android/os/VibratorInfo.java
+++ b/core/java/android/os/VibratorInfo.java
@@ -16,7 +16,6 @@
package android.os;
-import android.annotation.FloatRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.hardware.vibrator.Braking;
@@ -26,6 +25,8 @@ import android.util.Range;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
+import com.android.internal.util.Preconditions;
+
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -56,7 +57,7 @@ public class VibratorInfo implements Parcelable {
private final int mPwlePrimitiveDurationMax;
private final int mPwleSizeMax;
private final float mQFactor;
- private final FrequencyMapping mFrequencyMapping;
+ private final FrequencyProfile mFrequencyProfile;
VibratorInfo(Parcel in) {
mId = in.readInt();
@@ -69,7 +70,15 @@ public class VibratorInfo implements Parcelable {
mPwlePrimitiveDurationMax = in.readInt();
mPwleSizeMax = in.readInt();
mQFactor = in.readFloat();
- mFrequencyMapping = in.readParcelable(VibratorInfo.class.getClassLoader());
+ mFrequencyProfile = FrequencyProfile.CREATOR.createFromParcel(in);
+ }
+
+ public VibratorInfo(int id, @NonNull VibratorInfo baseVibratorInfo) {
+ this(id, baseVibratorInfo.mCapabilities, baseVibratorInfo.mSupportedEffects,
+ baseVibratorInfo.mSupportedBraking, baseVibratorInfo.mSupportedPrimitives,
+ baseVibratorInfo.mPrimitiveDelayMax, baseVibratorInfo.mCompositionSizeMax,
+ baseVibratorInfo.mPwlePrimitiveDurationMax, baseVibratorInfo.mPwleSizeMax,
+ baseVibratorInfo.mQFactor, baseVibratorInfo.mFrequencyProfile);
}
/**
@@ -92,7 +101,7 @@ public class VibratorInfo implements Parcelable {
* @param pwleSizeMax The maximum number of primitives supported by a PWLE
* composition.
* @param qFactor The vibrator quality factor.
- * @param frequencyMapping The description of the vibrator supported frequencies and max
+ * @param frequencyProfile The description of the vibrator supported frequencies and max
* amplitude mappings.
* @hide
*/
@@ -100,7 +109,9 @@ public class VibratorInfo implements Parcelable {
@Nullable SparseBooleanArray supportedBraking,
@NonNull SparseIntArray supportedPrimitives, int primitiveDelayMax,
int compositionSizeMax, int pwlePrimitiveDurationMax, int pwleSizeMax,
- float qFactor, @NonNull FrequencyMapping frequencyMapping) {
+ float qFactor, @NonNull FrequencyProfile frequencyProfile) {
+ Preconditions.checkNotNull(supportedPrimitives);
+ Preconditions.checkNotNull(frequencyProfile);
mId = id;
mCapabilities = capabilities;
mSupportedEffects = supportedEffects == null ? null : supportedEffects.clone();
@@ -111,14 +122,7 @@ public class VibratorInfo implements Parcelable {
mPwlePrimitiveDurationMax = pwlePrimitiveDurationMax;
mPwleSizeMax = pwleSizeMax;
mQFactor = qFactor;
- mFrequencyMapping = frequencyMapping;
- }
-
- protected VibratorInfo(int id, int capabilities, VibratorInfo baseVibrator) {
- this(id, capabilities, baseVibrator.mSupportedEffects, baseVibrator.mSupportedBraking,
- baseVibrator.mSupportedPrimitives, baseVibrator.mPrimitiveDelayMax,
- baseVibrator.mCompositionSizeMax, baseVibrator.mPwlePrimitiveDurationMax,
- baseVibrator.mPwleSizeMax, baseVibrator.mQFactor, baseVibrator.mFrequencyMapping);
+ mFrequencyProfile = frequencyProfile;
}
@Override
@@ -133,7 +137,7 @@ public class VibratorInfo implements Parcelable {
dest.writeInt(mPwlePrimitiveDurationMax);
dest.writeInt(mPwleSizeMax);
dest.writeFloat(mQFactor);
- dest.writeParcelable(mFrequencyMapping, flags);
+ mFrequencyProfile.writeToParcel(dest, flags);
}
@Override
@@ -170,13 +174,13 @@ public class VibratorInfo implements Parcelable {
&& Objects.equals(mSupportedEffects, that.mSupportedEffects)
&& Objects.equals(mSupportedBraking, that.mSupportedBraking)
&& Objects.equals(mQFactor, that.mQFactor)
- && Objects.equals(mFrequencyMapping, that.mFrequencyMapping);
+ && Objects.equals(mFrequencyProfile, that.mFrequencyProfile);
}
@Override
public int hashCode() {
int hashCode = Objects.hash(mId, mCapabilities, mSupportedEffects, mSupportedBraking,
- mQFactor, mFrequencyMapping);
+ mQFactor, mFrequencyProfile);
for (int i = 0; i < mSupportedPrimitives.size(); i++) {
hashCode = 31 * hashCode + mSupportedPrimitives.keyAt(i);
hashCode = 31 * hashCode + mSupportedPrimitives.valueAt(i);
@@ -198,7 +202,7 @@ public class VibratorInfo implements Parcelable {
+ ", mPwlePrimitiveDurationMax=" + mPwlePrimitiveDurationMax
+ ", mPwleSizeMax=" + mPwleSizeMax
+ ", mQFactor=" + mQFactor
- + ", mFrequencyMapping=" + mFrequencyMapping
+ + ", mFrequencyProfile=" + mFrequencyProfile
+ '}';
}
@@ -234,6 +238,30 @@ public class VibratorInfo implements Parcelable {
return Braking.NONE;
}
+ /** @hide */
+ @Nullable
+ public SparseBooleanArray getSupportedBraking() {
+ if (mSupportedBraking == null) {
+ return null;
+ }
+ return mSupportedBraking.clone();
+ }
+
+ /** @hide */
+ public boolean isBrakingSupportKnown() {
+ return mSupportedBraking != null;
+ }
+
+ /** @hide */
+ public boolean hasBrakingSupport(@Braking int braking) {
+ return (mSupportedBraking != null) && mSupportedBraking.get(braking);
+ }
+
+ /** @hide */
+ public boolean isEffectSupportKnown() {
+ return mSupportedEffects != null;
+ }
+
/**
* Query whether the vibrator supports the given effect.
*
@@ -252,6 +280,15 @@ public class VibratorInfo implements Parcelable {
: Vibrator.VIBRATION_EFFECT_SUPPORT_NO;
}
+ /** @hide */
+ @Nullable
+ public SparseBooleanArray getSupportedEffects() {
+ if (mSupportedEffects == null) {
+ return null;
+ }
+ return mSupportedEffects.clone();
+ }
+
/**
* Query whether the vibrator supports the given primitive.
*
@@ -276,6 +313,11 @@ public class VibratorInfo implements Parcelable {
return mSupportedPrimitives.get(primitiveId);
}
+ /** @hide */
+ public SparseIntArray getSupportedPrimitives() {
+ return mSupportedPrimitives.clone();
+ }
+
/**
* Query the maximum delay supported for a primitive in a composed effect.
*
@@ -329,8 +371,8 @@ public class VibratorInfo implements Parcelable {
* @return the resonant frequency of the vibrator, or {@link Float#NaN NaN} if it's unknown or
* this vibrator is a composite of multiple physical devices.
*/
- public float getResonantFrequency() {
- return mFrequencyMapping.mResonantFrequencyHz;
+ public float getResonantFrequencyHz() {
+ return mFrequencyProfile.mResonantFrequencyHz;
}
/**
@@ -344,31 +386,14 @@ public class VibratorInfo implements Parcelable {
}
/**
- * Return a range of frequency values supported by the vibrator.
+ * Gets the profile of supported frequencies, including the measurements of maximum relative
+ * output acceleration for supported vibration frequencies.
*
- * @return A range of frequency values supported, in hertz. The range will always contain the
- * device resonant frequency. Devices without frequency control will return null.
- * @hide
- */
- @Nullable
- public Range<Float> getFrequencyRangeHz() {
- return mFrequencyMapping.mFrequencyRangeHz;
- }
-
- /**
- * Return the maximum amplitude the vibrator can play at given frequency.
- *
- * @param frequencyHz The frequency, in hertz, for query.
-
- * @return a value in [0,1] representing the maximum amplitude the device can play at given
- * frequency. Devices without frequency control will return 0 to any input. Devices with
- * frequency control will return the supported value, for input in
- * {@link #getFrequencyRangeHz()}, and 0 for any other input.
- * @hide
+ * <p>If the devices does not have frequency control then the profile should be empty.
*/
- @FloatRange(from = 0, to = 1)
- public float getMaxAmplitude(float frequencyHz) {
- return mFrequencyMapping.getMaxAmplitude(frequencyHz);
+ @NonNull
+ public FrequencyProfile getFrequencyProfile() {
+ return mFrequencyProfile;
}
protected long getCapabilities() {
@@ -452,7 +477,7 @@ public class VibratorInfo implements Parcelable {
* Describes the maximum relative output acceleration that can be achieved for each supported
* frequency in a specific vibrator.
*
- * <p>This mapping is defined by the following parameters:
+ * <p>This profile is defined by the following parameters:
*
* <ol>
* <li>{@code minFrequencyHz}, {@code resonantFrequencyHz} and {@code frequencyResolutionHz}
@@ -466,7 +491,7 @@ public class VibratorInfo implements Parcelable {
*
* @hide
*/
- public static final class FrequencyMapping implements Parcelable {
+ public static final class FrequencyProfile implements Parcelable {
@Nullable
private final Range<Float> mFrequencyRangeHz;
private final float mMinFrequencyHz;
@@ -474,7 +499,7 @@ public class VibratorInfo implements Parcelable {
private final float mFrequencyResolutionHz;
private final float[] mMaxAmplitudes;
- FrequencyMapping(Parcel in) {
+ FrequencyProfile(Parcel in) {
this(in.readFloat(), in.readFloat(), in.readFloat(), in.createFloatArray());
}
@@ -484,13 +509,13 @@ public class VibratorInfo implements Parcelable {
* @param resonantFrequencyHz The vibrator resonant frequency, in hertz.
* @param minFrequencyHz Minimum supported frequency, in hertz.
* @param frequencyResolutionHz The frequency resolution, in hertz, used by the max
- * amplitudes mapping.
+ * amplitude measurements.
* @param maxAmplitudes The max amplitude supported by each supported frequency,
* starting at minimum frequency with jumps of frequency
* resolution.
* @hide
*/
- public FrequencyMapping(float resonantFrequencyHz, float minFrequencyHz,
+ public FrequencyProfile(float resonantFrequencyHz, float minFrequencyHz,
float frequencyResolutionHz, float[] maxAmplitudes) {
mMinFrequencyHz = minFrequencyHz;
mResonantFrequencyHz = resonantFrequencyHz;
@@ -500,18 +525,25 @@ public class VibratorInfo implements Parcelable {
System.arraycopy(maxAmplitudes, 0, mMaxAmplitudes, 0, maxAmplitudes.length);
}
- // If any required field is undefined then leave this mapping empty.
+ // If any required field is undefined or has a bad value then this profile is invalid.
boolean isValid = !Float.isNaN(resonantFrequencyHz)
+ && (resonantFrequencyHz > 0)
&& !Float.isNaN(minFrequencyHz)
+ && (minFrequencyHz > 0)
&& !Float.isNaN(frequencyResolutionHz)
+ && (frequencyResolutionHz > 0)
&& (mMaxAmplitudes.length > 0);
+ // If any max amplitude is outside the allowed range then this profile is invalid.
+ for (int i = 0; i < mMaxAmplitudes.length; i++) {
+ isValid &= (mMaxAmplitudes[i] >= 0) && (mMaxAmplitudes[i] <= 1);
+ }
+
float maxFrequencyHz = isValid
? minFrequencyHz + frequencyResolutionHz * (mMaxAmplitudes.length - 1)
: Float.NaN;
- // If the non-empty mapping does not have min < resonant < max frequency respected
- // then leave this mapping empty.
+ // If the constraint min < resonant < max is not met then it is invalid.
isValid &= !Float.isNaN(maxFrequencyHz)
&& (resonantFrequencyHz >= minFrequencyHz)
&& (resonantFrequencyHz <= maxFrequencyHz)
@@ -520,14 +552,17 @@ public class VibratorInfo implements Parcelable {
mFrequencyRangeHz = isValid ? Range.create(minFrequencyHz, maxFrequencyHz) : null;
}
- /**
- * Returns true if this frequency mapping is empty, i.e. the only supported is the resonant
- * frequency.
- */
+ /** Returns true if the supported frequency range is empty. */
public boolean isEmpty() {
return mFrequencyRangeHz == null;
}
+ /** Returns the supported frequency range, in hertz. */
+ @Nullable
+ public Range<Float> getFrequencyRangeHz() {
+ return mFrequencyRangeHz;
+ }
+
/**
* Returns the maximum relative amplitude the vibrator can reach while playing at the
* given frequency.
@@ -535,24 +570,43 @@ public class VibratorInfo implements Parcelable {
* @param frequencyHz frequency, in hertz, for query.
* @return A value in [0,1] representing the max relative amplitude supported at the given
* frequency. This will return 0 if the frequency is outside the supported range, or if the
- * mapping is empty.
+ * supported frequency range is empty.
*/
public float getMaxAmplitude(float frequencyHz) {
- if (isEmpty() || Float.isNaN(frequencyHz)) {
+ if (isEmpty() || Float.isNaN(frequencyHz) || !mFrequencyRangeHz.contains(frequencyHz)) {
// Unsupported frequency requested, vibrator cannot play at this frequency.
return 0;
}
- float position = (frequencyHz - mMinFrequencyHz) / mFrequencyResolutionHz;
- int floorIndex = (int) Math.floor(position);
- int ceilIndex = (int) Math.ceil(position);
- if (floorIndex < 0 || floorIndex >= mMaxAmplitudes.length) {
- return 0;
- }
- if (floorIndex != ceilIndex && ceilIndex < mMaxAmplitudes.length) {
- // Value in between two mapped frequency values, use the lowest supported one.
- return MathUtils.min(mMaxAmplitudes[floorIndex], mMaxAmplitudes[ceilIndex]);
- }
- return mMaxAmplitudes[floorIndex];
+
+ // Subtract minFrequencyHz to simplify offset calculations.
+ float mappingFreq = frequencyHz - mMinFrequencyHz;
+
+ // Find the bucket to interpolate within.
+ // Any calculated index should be safe, except exactly equal to max amplitude can be
+ // one step too high, so constrain it to guarantee safety.
+ int startIdx = MathUtils.constrain(
+ /* amount= */ (int) Math.floor(mappingFreq / mFrequencyResolutionHz),
+ /* low= */ 0, /* high= */ mMaxAmplitudes.length - 1);
+ int nextIdx = MathUtils.constrain(
+ /* amount= */ startIdx + 1,
+ /* low= */ 0, /* high= */ mMaxAmplitudes.length - 1);
+
+ // Linearly interpolate the amplitudes based on the frequency range of the bucket.
+ return MathUtils.constrainedMap(
+ mMaxAmplitudes[startIdx], mMaxAmplitudes[nextIdx],
+ startIdx * mFrequencyResolutionHz, nextIdx * mFrequencyResolutionHz,
+ mappingFreq);
+ }
+
+ /** Returns the raw list of maximum relative output accelerations from the vibrator. */
+ @NonNull
+ public float[] getMaxAmplitudes() {
+ return Arrays.copyOf(mMaxAmplitudes, mMaxAmplitudes.length);
+ }
+
+ /** Returns the raw frequency resolution used for max amplitude measurements, in hertz. */
+ public float getFrequencyResolutionHz() {
+ return mFrequencyResolutionHz;
}
@Override
@@ -573,10 +627,10 @@ public class VibratorInfo implements Parcelable {
if (this == o) {
return true;
}
- if (!(o instanceof FrequencyMapping)) {
+ if (!(o instanceof FrequencyProfile)) {
return false;
}
- FrequencyMapping that = (FrequencyMapping) o;
+ FrequencyProfile that = (FrequencyProfile) o;
return Float.compare(mMinFrequencyHz, that.mMinFrequencyHz) == 0
&& Float.compare(mResonantFrequencyHz, that.mResonantFrequencyHz) == 0
&& Float.compare(mFrequencyResolutionHz, that.mFrequencyResolutionHz) == 0
@@ -593,7 +647,7 @@ public class VibratorInfo implements Parcelable {
@Override
public String toString() {
- return "FrequencyMapping{"
+ return "FrequencyProfile{"
+ "mFrequencyRange=" + mFrequencyRangeHz
+ ", mMinFrequency=" + mMinFrequencyHz
+ ", mResonantFrequency=" + mResonantFrequencyHz
@@ -603,16 +657,16 @@ public class VibratorInfo implements Parcelable {
}
@NonNull
- public static final Creator<FrequencyMapping> CREATOR =
- new Creator<FrequencyMapping>() {
+ public static final Creator<FrequencyProfile> CREATOR =
+ new Creator<FrequencyProfile>() {
@Override
- public FrequencyMapping createFromParcel(Parcel in) {
- return new FrequencyMapping(in);
+ public FrequencyProfile createFromParcel(Parcel in) {
+ return new FrequencyProfile(in);
}
@Override
- public FrequencyMapping[] newArray(int size) {
- return new FrequencyMapping[size];
+ public FrequencyProfile[] newArray(int size) {
+ return new FrequencyProfile[size];
}
};
}
@@ -629,8 +683,8 @@ public class VibratorInfo implements Parcelable {
private int mPwlePrimitiveDurationMax;
private int mPwleSizeMax;
private float mQFactor = Float.NaN;
- private FrequencyMapping mFrequencyMapping =
- new FrequencyMapping(Float.NaN, Float.NaN, Float.NaN, null);
+ private FrequencyProfile mFrequencyProfile =
+ new FrequencyProfile(Float.NaN, Float.NaN, Float.NaN, null);
/** A builder class for a {@link VibratorInfo}. */
public Builder(int id) {
@@ -702,8 +756,8 @@ public class VibratorInfo implements Parcelable {
/** Configure the vibrator frequency information like resonant frequency and bandwidth. */
@NonNull
- public Builder setFrequencyMapping(FrequencyMapping frequencyMapping) {
- mFrequencyMapping = frequencyMapping;
+ public Builder setFrequencyProfile(@NonNull FrequencyProfile frequencyProfile) {
+ mFrequencyProfile = frequencyProfile;
return this;
}
@@ -712,7 +766,7 @@ public class VibratorInfo implements Parcelable {
public VibratorInfo build() {
return new VibratorInfo(mId, mCapabilities, mSupportedEffects, mSupportedBraking,
mSupportedPrimitives, mPrimitiveDelayMax, mCompositionSizeMax,
- mPwlePrimitiveDurationMax, mPwleSizeMax, mQFactor, mFrequencyMapping);
+ mPwlePrimitiveDurationMax, mPwleSizeMax, mQFactor, mFrequencyProfile);
}
/**
diff --git a/core/java/android/os/WorkSource.java b/core/java/android/os/WorkSource.java
index 6588b5748d09..e899f7729efa 100644
--- a/core/java/android/os/WorkSource.java
+++ b/core/java/android/os/WorkSource.java
@@ -130,7 +130,7 @@ public class WorkSource implements Parcelable {
int numChains = in.readInt();
if (numChains > 0) {
mChains = new ArrayList<>(numChains);
- in.readParcelableList(mChains, WorkChain.class.getClassLoader());
+ in.readParcelableList(mChains, WorkChain.class.getClassLoader(), android.os.WorkSource.WorkChain.class);
} else {
mChains = null;
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/ISmartspaceTransitionController.aidl b/core/java/android/os/logcat/ILogcatManagerService.aidl
index 2b3e961ab3b2..68b5679919d6 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/ISmartspaceTransitionController.aidl
+++ b/core/java/android/os/logcat/ILogcatManagerService.aidl
@@ -14,11 +14,13 @@
* limitations under the License.
*/
-package com.android.systemui.shared.system.smartspace;
+package android.os.logcat;
-import com.android.systemui.shared.system.smartspace.ISmartspaceCallback;
+/**
+ * @hide
+ */
+interface ILogcatManagerService {
+ void startThread(in int uid, in int gid, in int pid, in int fd);
+ void finishThread(in int uid, in int gid, in int pid, in int fd);
+}
-// Controller that keeps track of SmartSpace instances in remote processes (such as Launcher).
-interface ISmartspaceTransitionController {
- oneway void setSmartspace(ISmartspaceCallback callback);
-} \ No newline at end of file
diff --git a/core/java/android/os/logcat/OWNERS b/core/java/android/os/logcat/OWNERS
new file mode 100644
index 000000000000..cb21a6fafd7e
--- /dev/null
+++ b/core/java/android/os/logcat/OWNERS
@@ -0,0 +1 @@
+include platform/frameworks/base:/services/core/java/com/android/server/logcat/OWNERS
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 29accb9b3187..8df659de5924 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -80,6 +80,7 @@ import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
import android.os.SystemProperties;
import android.os.UserHandle;
+import android.provider.DeviceConfig;
import android.provider.MediaStore;
import android.provider.Settings;
import android.sysprop.VoldProperties;
@@ -1443,28 +1444,39 @@ public class StorageManager {
*
* @hide
*/
- public static final int STORAGE_THRESHOLD_PERCENT_HIGH = 20;
+ public static final int DEFAULT_STORAGE_THRESHOLD_PERCENT_HIGH = 20;
+ /** {@hide} */
+ @TestApi
+ public static final String
+ STORAGE_THRESHOLD_PERCENT_HIGH_KEY = "storage_threshold_percent_high";
/**
* Devices having below STORAGE_THRESHOLD_PERCENT_LOW of total space free are considered to be
- * in low free space category.
+ * in low free space category and can be configured via
+ * Settings.Global.SYS_STORAGE_THRESHOLD_PERCENTAGE.
*
* @hide
*/
- public static final int STORAGE_THRESHOLD_PERCENT_LOW = 5;
+ public static final int DEFAULT_STORAGE_THRESHOLD_PERCENT_LOW = 5;
/**
* For devices in high free space category, CACHE_RESERVE_PERCENT_HIGH percent of total space is
* allocated for cache.
*
* @hide
*/
- public static final int CACHE_RESERVE_PERCENT_HIGH = 10;
+ public static final int DEFAULT_CACHE_RESERVE_PERCENT_HIGH = 10;
+ /** {@hide} */
+ @TestApi
+ public static final String CACHE_RESERVE_PERCENT_HIGH_KEY = "cache_reserve_percent_high";
/**
* For devices in low free space category, CACHE_RESERVE_PERCENT_LOW percent of total space is
* allocated for cache.
*
* @hide
*/
- public static final int CACHE_RESERVE_PERCENT_LOW = 2;
+ public static final int DEFAULT_CACHE_RESERVE_PERCENT_LOW = 2;
+ /** {@hide} */
+ @TestApi
+ public static final String CACHE_RESERVE_PERCENT_LOW_KEY = "cache_reserve_percent_low";
private static final long DEFAULT_THRESHOLD_MAX_BYTES = DataUnit.MEBIBYTES.toBytes(500);
@@ -1490,7 +1502,8 @@ public class StorageManager {
@UnsupportedAppUsage
public long getStorageLowBytes(File path) {
final long lowPercent = Settings.Global.getInt(mResolver,
- Settings.Global.SYS_STORAGE_THRESHOLD_PERCENTAGE, STORAGE_THRESHOLD_PERCENT_LOW);
+ Settings.Global.SYS_STORAGE_THRESHOLD_PERCENTAGE,
+ DEFAULT_STORAGE_THRESHOLD_PERCENT_LOW);
final long lowBytes = (path.getTotalSpace() * lowPercent) / 100;
final long maxLowBytes = Settings.Global.getLong(mResolver,
@@ -1510,24 +1523,33 @@ public class StorageManager {
@TestApi
@SuppressLint("StreamFiles")
public long computeStorageCacheBytes(@NonNull File path) {
+ final int storageThresholdPercentHigh = DeviceConfig.getInt(
+ DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+ STORAGE_THRESHOLD_PERCENT_HIGH_KEY, DEFAULT_STORAGE_THRESHOLD_PERCENT_HIGH);
+ final int cacheReservePercentHigh = DeviceConfig.getInt(
+ DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+ CACHE_RESERVE_PERCENT_HIGH_KEY, DEFAULT_CACHE_RESERVE_PERCENT_HIGH);
+ final int cacheReservePercentLow = DeviceConfig.getInt(
+ DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+ CACHE_RESERVE_PERCENT_LOW_KEY, DEFAULT_CACHE_RESERVE_PERCENT_LOW);
final long totalBytes = path.getTotalSpace();
final long usableBytes = path.getUsableSpace();
- final long storageThresholdHighBytes = totalBytes * STORAGE_THRESHOLD_PERCENT_HIGH / 100;
+ final long storageThresholdHighBytes = totalBytes * storageThresholdPercentHigh / 100;
final long storageThresholdLowBytes = getStorageLowBytes(path);
long result;
if (usableBytes > storageThresholdHighBytes) {
- // If free space is >STORAGE_THRESHOLD_PERCENT_HIGH of total space,
- // reserve CACHE_RESERVE_PERCENT_HIGH of total space
- result = totalBytes * CACHE_RESERVE_PERCENT_HIGH / 100;
+ // If free space is >storageThresholdPercentHigh of total space,
+ // reserve cacheReservePercentHigh of total space
+ result = totalBytes * cacheReservePercentHigh / 100;
} else if (usableBytes < storageThresholdLowBytes) {
- // If free space is <min(STORAGE_THRESHOLD_PERCENT_LOW of total space, 500MB),
- // reserve CACHE_RESERVE_PERCENT_LOW of total space
- result = totalBytes * CACHE_RESERVE_PERCENT_LOW / 100;
+ // If free space is <min(storageThresholdPercentLow of total space, 500MB),
+ // reserve cacheReservePercentLow of total space
+ result = totalBytes * cacheReservePercentLow / 100;
} else {
// Else, linearly interpolate the amount of space to reserve
- double slope = (CACHE_RESERVE_PERCENT_HIGH - CACHE_RESERVE_PERCENT_LOW) * totalBytes
+ double slope = (cacheReservePercentHigh - cacheReservePercentLow) * totalBytes
/ (100.0 * (storageThresholdHighBytes - storageThresholdLowBytes));
- double intercept = totalBytes * CACHE_RESERVE_PERCENT_LOW / 100.0
+ double intercept = totalBytes * cacheReservePercentLow / 100.0
- storageThresholdLowBytes * slope;
result = Math.round(slope * usableBytes + intercept);
}
diff --git a/core/java/android/os/storage/StorageVolume.java b/core/java/android/os/storage/StorageVolume.java
index b78bb253bcf7..8ee52c21e869 100644
--- a/core/java/android/os/storage/StorageVolume.java
+++ b/core/java/android/os/storage/StorageVolume.java
@@ -168,7 +168,7 @@ public final class StorageVolume implements Parcelable {
mExternallyManaged = in.readInt() != 0;
mAllowMassStorage = in.readInt() != 0;
mMaxFileSize = in.readLong();
- mOwner = in.readParcelable(null);
+ mOwner = in.readParcelable(null, android.os.UserHandle.class);
if (in.readInt() != 0) {
mUuid = StorageManager.convert(in.readString8());
} else {
diff --git a/core/java/android/os/vibrator/VibratorFrequencyProfile.java b/core/java/android/os/vibrator/VibratorFrequencyProfile.java
new file mode 100644
index 000000000000..23b45aed8eb4
--- /dev/null
+++ b/core/java/android/os/vibrator/VibratorFrequencyProfile.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.vibrator;
+
+import android.annotation.FloatRange;
+import android.annotation.NonNull;
+import android.os.VibratorInfo;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * Describes the output of a {@link android.os.Vibrator} for different vibration frequencies.
+ *
+ * <p>The profile contains the minimum and maximum supported vibration frequencies, if the device
+ * supports independent frequency control.
+ *
+ * <p>It also describes the relative output acceleration of a vibration at different supported
+ * frequencies. The acceleration is defined by a relative amplitude value between 0 and 1,
+ * inclusive, where 0 represents the vibrator off state and 1 represents the maximum output
+ * acceleration that the vibrator can reach across all supported frequencies.
+ *
+ * <p>The measurements are returned as an array of uniformly distributed amplitude values for
+ * frequencies between the minimum and maximum supported ones. The measurement interval is the
+ * frequency increment between each pair of amplitude values.
+ *
+ * <p>Vibrators without independent frequency control do not have a frequency profile.
+ */
+public final class VibratorFrequencyProfile {
+
+ private final VibratorInfo.FrequencyProfile mFrequencyProfile;
+
+ /** @hide */
+ public VibratorFrequencyProfile(@NonNull VibratorInfo.FrequencyProfile frequencyProfile) {
+ Preconditions.checkArgument(!frequencyProfile.isEmpty(),
+ "Frequency profile must have a non-empty frequency range");
+ mFrequencyProfile = frequencyProfile;
+ }
+
+ /**
+ * Measurements of the maximum relative amplitude the vibrator can achieve for each supported
+ * frequency.
+ *
+ * <p>The frequency of a measurement is determined as:
+ *
+ * {@code getMinFrequency() + measurementIndex * getMaxAmplitudeMeasurementInterval()}
+ *
+ * <p>The returned list will not be empty, and will have entries representing frequencies from
+ * {@link #getMinFrequency()} to {@link #getMaxFrequency()}, inclusive.
+ *
+ * @return Array of maximum relative amplitude measurements, each value is between 0 and 1,
+ * inclusive.
+ */
+ @NonNull
+ @FloatRange(from = 0, to = 1)
+ public float[] getMaxAmplitudeMeasurements() {
+ // VibratorInfo getters always return a copy or clone of the data objects.
+ return mFrequencyProfile.getMaxAmplitudes();
+ }
+
+ /**
+ * Gets the frequency interval used to measure the maximum relative amplitudes.
+ *
+ * @return the frequency interval used for the measurement, in hertz.
+ */
+ public float getMaxAmplitudeMeasurementInterval() {
+ return mFrequencyProfile.getFrequencyResolutionHz();
+ }
+
+ /**
+ * Gets the minimum frequency supported by the vibrator.
+ *
+ * @return the minimum frequency supported by the vibrator, in hertz.
+ */
+ public float getMinFrequency() {
+ return mFrequencyProfile.getFrequencyRangeHz().getLower();
+ }
+
+ /**
+ * Gets the maximum frequency supported by the vibrator.
+ *
+ * @return the maximum frequency supported by the vibrator, in hertz.
+ */
+ public float getMaxFrequency() {
+ return mFrequencyProfile.getFrequencyRangeHz().getUpper();
+ }
+}
diff --git a/core/java/android/permission/IPermissionController.aidl b/core/java/android/permission/IPermissionController.aidl
index 5814bac06f59..c9dd06cfaa43 100644
--- a/core/java/android/permission/IPermissionController.aidl
+++ b/core/java/android/permission/IPermissionController.aidl
@@ -56,6 +56,9 @@ oneway interface IPermissionController {
in AndroidFuture<String> callback);
void getUnusedAppCount(
in AndroidFuture callback);
- void selfRevokePermissions(in String packageName, in List<String> permissions,
+ void getHibernationEligibility(
+ in String packageName,
+ in AndroidFuture callback);
+ void revokeOwnPermissionsOnKill(in String packageName, in List<String> permissions,
in AndroidFuture callback);
}
diff --git a/core/java/android/permission/IPermissionManager.aidl b/core/java/android/permission/IPermissionManager.aidl
index 8e5581b1b80e..1c0320e9a86e 100644
--- a/core/java/android/permission/IPermissionManager.aidl
+++ b/core/java/android/permission/IPermissionManager.aidl
@@ -76,7 +76,7 @@ interface IPermissionManager {
List<SplitPermissionInfoParcelable> getSplitPermissions();
- void selfRevokePermissions(String packageName, in List<String> permissions);
+ void revokeOwnPermissionsOnKill(String packageName, in List<String> permissions);
void startOneTimePermissionSession(String packageName, int userId, long timeout,
int importanceToResetTimer, int importanceToKeepSessionAlive);
diff --git a/core/java/android/permission/PermissionControllerManager.java b/core/java/android/permission/PermissionControllerManager.java
index 47cd10765da0..0cf06aa364ec 100644
--- a/core/java/android/permission/PermissionControllerManager.java
+++ b/core/java/android/permission/PermissionControllerManager.java
@@ -128,6 +128,51 @@ public final class PermissionControllerManager {
/** Count and app even if it is a system app. */
public static final int COUNT_WHEN_SYSTEM = 2;
+ /** @hide */
+ @IntDef(prefix = { "HIBERNATION_ELIGIBILITY_"}, value = {
+ HIBERNATION_ELIGIBILITY_UNKNOWN,
+ HIBERNATION_ELIGIBILITY_ELIGIBLE,
+ HIBERNATION_ELIGIBILITY_EXEMPT_BY_SYSTEM,
+ HIBERNATION_ELIGIBILITY_EXEMPT_BY_USER,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface HibernationEligibilityFlag {}
+
+ /**
+ * Unknown whether package is eligible for hibernation.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int HIBERNATION_ELIGIBILITY_UNKNOWN = -1;
+
+ /**
+ * Package is eligible for app hibernation and may be hibernated when the job runs.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int HIBERNATION_ELIGIBILITY_ELIGIBLE = 0;
+
+ /**
+ * Package is not eligible for app hibernation because it is categorically exempt via the
+ * system.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int HIBERNATION_ELIGIBILITY_EXEMPT_BY_SYSTEM = 1;
+
+ /**
+ * Package is not eligible for app hibernation because it has been exempt by the user's
+ * preferences. Note that this should not be set if the package is exempt from hibernation by
+ * the system as the user preference would have no effect.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int HIBERNATION_ELIGIBILITY_EXEMPT_BY_USER = 2;
+
/**
* Callback for delivering the result of {@link #revokeRuntimePermissions}.
*/
@@ -819,6 +864,39 @@ public final class PermissionControllerManager {
}
/**
+ * Get the hibernation eligibility of a package. See {@link HibernationEligibilityFlag}.
+ *
+ * @param packageName package name to check eligibility
+ * @param executor executor to run callback on
+ * @param callback callback for when result is generated
+ */
+ @RequiresPermission(Manifest.permission.MANAGE_APP_HIBERNATION)
+ public void getHibernationEligibility(@NonNull String packageName,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull IntConsumer callback) {
+ checkNotNull(executor);
+ checkNotNull(callback);
+
+ mRemoteService.postAsync(service -> {
+ AndroidFuture<Integer> eligibilityResult = new AndroidFuture<>();
+ service.getHibernationEligibility(packageName, eligibilityResult);
+ return eligibilityResult;
+ }).whenCompleteAsync((eligibility, err) -> {
+ if (err != null) {
+ Log.e(TAG, "Error getting hibernation eligibility", err);
+ callback.accept(HIBERNATION_ELIGIBILITY_UNKNOWN);
+ } else {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ callback.accept(eligibility);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ }, executor);
+ }
+
+ /**
* Triggers the revocation of one or more permissions for a package, under the following
* conditions:
* <ul>
@@ -835,17 +913,17 @@ public final class PermissionControllerManager {
*
* @param packageName The name of the package for which the permissions will be revoked.
* @param permissions List of permissions to be revoked.
+ * @param callback Callback called when the revocation request has been completed.
*
- * @see Context#selfRevokePermissions(Collection)
+ * @see Context#revokeOwnPermissionsOnKill(Collection)
*
* @hide
*/
- public void selfRevokePermissions(@NonNull String packageName,
- @NonNull List<String> permissions) {
+ public void revokeOwnPermissionsOnKill(@NonNull String packageName,
+ @NonNull List<String> permissions, AndroidFuture<Void> callback) {
mRemoteService.postAsync(service -> {
- AndroidFuture<Void> future = new AndroidFuture<>();
- service.selfRevokePermissions(packageName, permissions, future);
- return future;
+ service.revokeOwnPermissionsOnKill(packageName, permissions, callback);
+ return callback;
}).whenComplete((result, err) -> {
if (err != null) {
Log.e(TAG, "Failed to self revoke " + String.join(",", permissions)
diff --git a/core/java/android/permission/PermissionControllerService.java b/core/java/android/permission/PermissionControllerService.java
index dcbab62530b1..8d9f82b04b54 100644
--- a/core/java/android/permission/PermissionControllerService.java
+++ b/core/java/android/permission/PermissionControllerService.java
@@ -337,10 +337,10 @@ public abstract class PermissionControllerService extends Service {
* @param permissions List of permissions to be revoked.
* @param callback Callback waiting for operation to be complete.
*
- * @see PermissionManager#selfRevokePermissions(java.util.Collection)
+ * @see PermissionManager#revokeOwnPermissionsOnKill(java.util.Collection)
*/
@BinderThread
- public void onSelfRevokePermissions(@NonNull String packageName,
+ public void onRevokeOwnPermissionsOnKill(@NonNull String packageName,
@NonNull List<String> permissions, @NonNull Runnable callback) {
throw new AbstractMethodError("Must be overridden in implementing class");
}
@@ -375,6 +375,22 @@ public abstract class PermissionControllerService extends Service {
throw new AbstractMethodError("Must be overridden in implementing class");
}
+ /**
+ * Get the hibernation eligibility of the app. See
+ * {@link android.permission.PermissionControllerManager.HibernationEligibilityFlag}.
+ *
+ * @param packageName package to check eligibility
+ * @param callback callback after eligibility is returned
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.MANAGE_APP_HIBERNATION)
+ public void onGetHibernationEligibility(@NonNull String packageName,
+ @NonNull IntConsumer callback) {
+ throw new AbstractMethodError("Must be overridden in implementing class");
+ }
+
@Override
public final @NonNull IBinder onBind(Intent intent) {
return new IPermissionController.Stub() {
@@ -669,13 +685,29 @@ public abstract class PermissionControllerService extends Service {
}
@Override
- public void selfRevokePermissions(@NonNull String packageName,
+ public void getHibernationEligibility(@NonNull String packageName,
+ @NonNull AndroidFuture callback) {
+ try {
+ Objects.requireNonNull(callback);
+
+ enforceSomePermissionsGrantedToCaller(
+ Manifest.permission.MANAGE_APP_HIBERNATION);
+
+ PermissionControllerService.this.onGetHibernationEligibility(packageName,
+ callback::complete);
+ } catch (Throwable t) {
+ callback.completeExceptionally(t);
+ }
+ }
+
+ @Override
+ public void revokeOwnPermissionsOnKill(@NonNull String packageName,
@NonNull List<String> permissions, @NonNull AndroidFuture callback) {
try {
enforceSomePermissionsGrantedToCaller(
Manifest.permission.REVOKE_RUNTIME_PERMISSIONS);
Objects.requireNonNull(callback);
- onSelfRevokePermissions(packageName, permissions,
+ onRevokeOwnPermissionsOnKill(packageName, permissions,
() -> callback.complete(null));
} catch (Throwable t) {
callback.completeExceptionally(t);
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index 13941dc5ef82..e4aee7619250 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -562,12 +562,12 @@ public final class PermissionManager {
}
/**
- * @see Context#selfRevokePermissions(Collection)
+ * @see Context#revokeOwnPermissionsOnKill(Collection)
* @hide
*/
- public void selfRevokePermissions(@NonNull Collection<String> permissions) {
+ public void revokeOwnPermissionsOnKill(@NonNull Collection<String> permissions) {
try {
- mPermissionManager.selfRevokePermissions(mContext.getPackageName(),
+ mPermissionManager.revokeOwnPermissionsOnKill(mContext.getPackageName(),
new ArrayList<String>(permissions));
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
diff --git a/core/java/android/print/PrintJobInfo.java b/core/java/android/print/PrintJobInfo.java
index 67249be2b806..9d0c8d82ed0d 100644
--- a/core/java/android/print/PrintJobInfo.java
+++ b/core/java/android/print/PrintJobInfo.java
@@ -231,9 +231,9 @@ public final class PrintJobInfo implements Parcelable {
}
private PrintJobInfo(@NonNull Parcel parcel) {
- mId = parcel.readParcelable(null);
+ mId = parcel.readParcelable(null, android.print.PrintJobId.class);
mLabel = parcel.readString();
- mPrinterId = parcel.readParcelable(null);
+ mPrinterId = parcel.readParcelable(null, android.print.PrinterId.class);
mPrinterName = parcel.readString();
mState = parcel.readInt();
mAppId = parcel.readInt();
@@ -247,8 +247,8 @@ public final class PrintJobInfo implements Parcelable {
mPageRanges[i] = (PageRange) parcelables[i];
}
}
- mAttributes = (PrintAttributes) parcel.readParcelable(null);
- mDocumentInfo = (PrintDocumentInfo) parcel.readParcelable(null);
+ mAttributes = (PrintAttributes) parcel.readParcelable(null, android.print.PrintAttributes.class);
+ mDocumentInfo = (PrintDocumentInfo) parcel.readParcelable(null, android.print.PrintDocumentInfo.class);
mProgress = parcel.readFloat();
mStatus = parcel.readCharSequence();
mStatusRes = parcel.readInt();
diff --git a/core/java/android/print/PrinterId.java b/core/java/android/print/PrinterId.java
index 25260c473709..284e122fc103 100644
--- a/core/java/android/print/PrinterId.java
+++ b/core/java/android/print/PrinterId.java
@@ -48,7 +48,7 @@ public final class PrinterId implements Parcelable {
}
private PrinterId(@NonNull Parcel parcel) {
- mServiceName = Preconditions.checkNotNull((ComponentName) parcel.readParcelable(null));
+ mServiceName = Preconditions.checkNotNull((ComponentName) parcel.readParcelable(null, android.content.ComponentName.class));
mLocalId = Preconditions.checkNotNull(parcel.readString());
}
diff --git a/core/java/android/print/PrinterInfo.java b/core/java/android/print/PrinterInfo.java
index 8e03e3eb3f22..2f93e404a211 100644
--- a/core/java/android/print/PrinterInfo.java
+++ b/core/java/android/print/PrinterInfo.java
@@ -270,15 +270,15 @@ public final class PrinterInfo implements Parcelable {
private PrinterInfo(Parcel parcel) {
// mName can be null due to unchecked set in Builder.setName and status can be invalid
// due to unchecked set in Builder.setStatus, hence we can only check mId for a valid state
- mId = checkPrinterId((PrinterId) parcel.readParcelable(null));
+ mId = checkPrinterId((PrinterId) parcel.readParcelable(null, android.print.PrinterId.class));
mName = checkName(parcel.readString());
mStatus = checkStatus(parcel.readInt());
mDescription = parcel.readString();
- mCapabilities = parcel.readParcelable(null);
+ mCapabilities = parcel.readParcelable(null, android.print.PrinterCapabilitiesInfo.class);
mIconResourceId = parcel.readInt();
mHasCustomPrinterIcon = parcel.readByte() != 0;
mCustomPrinterIconGen = parcel.readInt();
- mInfoIntent = parcel.readParcelable(null);
+ mInfoIntent = parcel.readParcelable(null, android.app.PendingIntent.class);
}
@Override
diff --git a/core/java/android/printservice/PrintServiceInfo.java b/core/java/android/printservice/PrintServiceInfo.java
index 0c1b61d583b3..347955718f78 100644
--- a/core/java/android/printservice/PrintServiceInfo.java
+++ b/core/java/android/printservice/PrintServiceInfo.java
@@ -76,7 +76,7 @@ public final class PrintServiceInfo implements Parcelable {
public PrintServiceInfo(Parcel parcel) {
mId = parcel.readString();
mIsEnabled = parcel.readByte() != 0;
- mResolveInfo = parcel.readParcelable(null);
+ mResolveInfo = parcel.readParcelable(null, android.content.pm.ResolveInfo.class);
mSettingsActivityName = parcel.readString();
mAddPrintersActivityName = parcel.readString();
mAdvancedPrintOptionsActivityName = parcel.readString();
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 6349cde2b8fc..87f4489e30f8 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -464,6 +464,13 @@ public final class DeviceConfig {
public static final String NAMESPACE_STORAGE_NATIVE_BOOT = "storage_native_boot";
/**
+ * Namespace for all Supplemental Api related features.
+ * @hide
+ */
+ @SystemApi
+ public static final String NAMESPACE_SUPPLEMENTAL_API = "supplemental_api";
+
+ /**
* Namespace for all SurfaceFlinger features that are used at the native level.
* These features are applied on boot or after reboot.
*
@@ -687,6 +694,15 @@ public final class DeviceConfig {
@SystemApi
public static final String NAMESPACE_UWB = "uwb";
+ /**
+ * Namespace for AmbientContextEventManagerService related features.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String NAMESPACE_AMBIENT_CONTEXT_MANAGER_SERVICE =
+ "ambient_context_manager_service";
+
private static final Object sLock = new Object();
@GuardedBy("sLock")
private static ArrayMap<OnPropertiesChangedListener, Pair<String, Executor>> sListeners =
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 5cdb1fdf64ce..3f544089fcf3 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5120,7 +5120,12 @@ public final class Settings {
* It was about AudioManager's setting and thus affected all the applications which
* relied on the setting, while this is purely about the vibration setting for incoming
* calls.
+ *
+ * @deprecated Replaced by using {@link android.os.VibrationAttributes#USAGE_RINGTONE} on
+ * vibrations for incoming calls. User settings are applied automatically by the service and
+ * should not be applied by individual apps.
*/
+ @Deprecated
@Readable
public static final String VIBRATE_WHEN_RINGING = "vibrate_when_ringing";
@@ -5181,7 +5186,12 @@ public final class Settings {
/**
* Whether haptic feedback (Vibrate on tap) is enabled. The value is
* boolean (1 or 0).
+ *
+ * @deprecated Replaced by using {@link android.os.VibrationAttributes#USAGE_TOUCH} on
+ * vibrations. User settings are applied automatically by the service and should not be
+ * applied by individual apps.
*/
+ @Deprecated
@Readable
public static final String HAPTIC_FEEDBACK_ENABLED = "haptic_feedback_enabled";
@@ -7158,6 +7168,12 @@ public final class Settings {
public static final String LOCATION_COARSE_ACCURACY_M = "locationCoarseAccuracy";
/**
+ * Whether or not to show display system location accesses.
+ * @hide
+ */
+ public static final String LOCATION_SHOW_SYSTEM_OPS = "locationShowSystemOps";
+
+ /**
* A flag containing settings used for biometric weak
* @hide
*/
@@ -8974,6 +8990,15 @@ public final class Settings {
public static final String UI_NIGHT_MODE = "ui_night_mode";
/**
+ * The current night mode custom type that has been selected by the user. Owned
+ * and controlled by UiModeManagerService. Constants are as per UiModeManager.
+ * @hide
+ */
+ @Readable
+ @SuppressLint("NoSettingsProvider")
+ public static final String UI_NIGHT_MODE_CUSTOM_TYPE = "ui_night_mode_custom_type";
+
+ /**
* The current night mode that has been overridden to turn on by the system. Owned
* and controlled by UiModeManagerService. Constants are as per
* UiModeManager.
@@ -9038,6 +9063,16 @@ public final class Settings {
public static final String SCREENSAVER_DEFAULT_COMPONENT = "screensaver_default_component";
/**
+ * The complications that are enabled to be shown over the screensaver by the user. Holds
+ * a comma separated list of
+ * {@link com.android.settingslib.dream.DreamBackend.ComplicationType}.
+ *
+ * @hide
+ */
+ public static final String SCREENSAVER_ENABLED_COMPLICATIONS =
+ "screensaver_enabled_complications";
+
+ /**
* The default NFC payment component
* @hide
*/
@@ -10567,6 +10602,14 @@ public final class Settings {
"communal_mode_trusted_networks";
/**
+ * Setting to allow Fast Pair scans to be enabled.
+ * @hide
+ */
+ @SystemApi
+ @Readable
+ public static final String FAST_PAIR_SCAN_ENABLED = "fast_pair_scan_enabled";
+
+ /**
* These entries are considered common between the personal and the managed profile,
* since the managed profile doesn't get to change them.
*/
@@ -12798,16 +12841,6 @@ public final class Settings {
SYS_STORAGE_CACHE_PERCENTAGE = "sys_storage_cache_percentage";
/**
- * Maximum bytes of storage on the device that is reserved for cached
- * data.
- *
- * @hide
- */
- @Readable
- public static final String
- SYS_STORAGE_CACHE_MAX_BYTES = "sys_storage_cache_max_bytes";
-
- /**
* The maximum reconnect delay for short network outages or when the
* network is suspended due to phone use.
*
diff --git a/core/java/android/service/ambientcontext/AmbientContextDetectionService.java b/core/java/android/service/ambientcontext/AmbientContextDetectionService.java
new file mode 100644
index 000000000000..dccfe3693b35
--- /dev/null
+++ b/core/java/android/service/ambientcontext/AmbientContextDetectionService.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.ambientcontext;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.app.ambientcontext.AmbientContextEvent;
+import android.app.ambientcontext.AmbientContextEventRequest;
+import android.app.ambientcontext.AmbientContextEventResponse;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteCallback;
+import android.util.Slog;
+
+import java.util.Objects;
+import java.util.function.Consumer;
+
+/**
+ * Abstract base class for {@link AmbientContextEvent} detection service.
+ *
+ * <p> A service that provides requested ambient context events to the system.
+ * The system's default AmbientContextDetectionService implementation is configured in
+ * {@code config_defaultAmbientContextDetectionService}. If this config has no value, a stub is
+ * returned.
+ *
+ * See: {@code AmbientContextManagerService}.
+ *
+ * <pre>
+ * {@literal
+ * <service android:name=".YourAmbientContextDetectionService"
+ * android:permission="android.permission.BIND_AMBIENT_CONTEXT_DETECTION_SERVICE">
+ * </service>}
+ * </pre>
+ *
+ * @hide
+ */
+@SystemApi
+public abstract class AmbientContextDetectionService extends Service {
+ private static final String TAG = AmbientContextDetectionService.class.getSimpleName();
+
+ /**
+ * The {@link Intent} that must be declared as handled by the service. To be supported, the
+ * service must also require the
+ * {@link android.Manifest.permission#BIND_AMBIENT_CONTEXT_DETECTION_SERVICE}
+ * permission so that other applications can not abuse it.
+ */
+ public static final String SERVICE_INTERFACE =
+ "android.service.ambientcontext.AmbientContextDetectionService";
+
+ /**
+ * The key for the bundle the parameter of {@code RemoteCallback#sendResult}. Implementation
+ * should set bundle result with this key.
+ *
+ * @hide
+ */
+ public static final String RESPONSE_BUNDLE_KEY =
+ "android.service.ambientcontext.EventResponseKey";
+
+ @Nullable
+ @Override
+ public final IBinder onBind(@NonNull Intent intent) {
+ if (SERVICE_INTERFACE.equals(intent.getAction())) {
+ return new IAmbientContextDetectionService.Stub() {
+ /** {@inheritDoc} */
+ @Override
+ public void startDetection(
+ @NonNull AmbientContextEventRequest request, String packageName,
+ RemoteCallback callback) {
+ Objects.requireNonNull(request);
+ Objects.requireNonNull(callback);
+ Consumer<AmbientContextEventResponse> consumer =
+ response -> {
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(
+ AmbientContextDetectionService.RESPONSE_BUNDLE_KEY,
+ response);
+ callback.sendResult(bundle);
+ };
+ AmbientContextDetectionService.this.onStartDetection(
+ request, packageName, consumer);
+ Slog.d(TAG, "startDetection " + request);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void stopDetection(String packageName) {
+ Objects.requireNonNull(packageName);
+ AmbientContextDetectionService.this.onStopDetection(packageName);
+ }
+ };
+ }
+ return null;
+ }
+
+ /**
+ * Starts detection and provides detected events to the consumer. The ongoing detection will
+ * keep running, until onStopDetection is called. If there were previously requested
+ * detection from the same package, the previous request will be replaced with the new request.
+ * The implementation should keep track of whether the user consented each requested
+ * AmbientContextEvent for the app. If not consented, the response should set status
+ * STATUS_ACCESS_DENIED and include an action PendingIntent for the app to redirect the user
+ * to the consent screen.
+ *
+ * @param request The request with events to detect, optional detection window and other
+ * options.
+ * @param packageName the requesting app's package name
+ * @param consumer the consumer for the detected event
+ */
+ public abstract void onStartDetection(
+ @NonNull AmbientContextEventRequest request,
+ @NonNull String packageName,
+ @NonNull Consumer<AmbientContextEventResponse> consumer);
+
+ /**
+ * Stops detection of the events. Events that are not being detected will be ignored.
+ *
+ * @param packageName stops detection for the given package.
+ */
+ public abstract void onStopDetection(@NonNull String packageName);
+}
diff --git a/core/java/android/service/ambientcontext/IAmbientContextDetectionService.aidl b/core/java/android/service/ambientcontext/IAmbientContextDetectionService.aidl
new file mode 100644
index 000000000000..1c6e25efeabe
--- /dev/null
+++ b/core/java/android/service/ambientcontext/IAmbientContextDetectionService.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.ambientcontext;
+
+import android.app.ambientcontext.AmbientContextEventRequest;
+import android.os.RemoteCallback;
+
+/**
+ * Interface for a concrete implementation to provide AmbientContextEvents to the framework.
+ *
+ * @hide
+ */
+oneway interface IAmbientContextDetectionService {
+ void startDetection(in AmbientContextEventRequest request, in String packageName,
+ in RemoteCallback callback);
+ void stopDetection(in String packageName);
+} \ No newline at end of file
diff --git a/core/java/android/service/ambientcontext/OWNERS b/core/java/android/service/ambientcontext/OWNERS
new file mode 100644
index 000000000000..a863297b84e8
--- /dev/null
+++ b/core/java/android/service/ambientcontext/OWNERS
@@ -0,0 +1,3 @@
+enxun@google.com
+kxchen@google.com
+tgadh@google.com
diff --git a/core/java/android/service/autofill/BatchUpdates.java b/core/java/android/service/autofill/BatchUpdates.java
index 8eeecc293104..c996cc088d66 100644
--- a/core/java/android/service/autofill/BatchUpdates.java
+++ b/core/java/android/service/autofill/BatchUpdates.java
@@ -205,7 +205,7 @@ public final class BatchUpdates implements Parcelable {
builder.transformChild(ids[i], values[i]);
}
}
- final RemoteViews updates = parcel.readParcelable(null);
+ final RemoteViews updates = parcel.readParcelable(null, android.widget.RemoteViews.class);
if (updates != null) {
builder.updateTemplate(updates);
}
diff --git a/core/java/android/service/autofill/CompositeUserData.java b/core/java/android/service/autofill/CompositeUserData.java
index 92952cb7dc24..55ac5a5e92f0 100644
--- a/core/java/android/service/autofill/CompositeUserData.java
+++ b/core/java/android/service/autofill/CompositeUserData.java
@@ -197,8 +197,8 @@ public final class CompositeUserData implements FieldClassificationUserData, Par
// Always go through the builder to ensure the data ingested by
// the system obeys the contract of the builder to avoid attacks
// using specially crafted parcels.
- final UserData genericUserData = parcel.readParcelable(null);
- final UserData packageUserData = parcel.readParcelable(null);
+ final UserData genericUserData = parcel.readParcelable(null, android.service.autofill.UserData.class);
+ final UserData packageUserData = parcel.readParcelable(null, android.service.autofill.UserData.class);
return new CompositeUserData(genericUserData, packageUserData);
}
diff --git a/core/java/android/service/autofill/CustomDescription.java b/core/java/android/service/autofill/CustomDescription.java
index f3f912bb3a5b..690cd0691631 100644
--- a/core/java/android/service/autofill/CustomDescription.java
+++ b/core/java/android/service/autofill/CustomDescription.java
@@ -437,7 +437,7 @@ public final class CustomDescription implements Parcelable {
// Always go through the builder to ensure the data ingested by
// the system obeys the contract of the builder to avoid attacks
// using specially crafted parcels.
- final RemoteViews parentPresentation = parcel.readParcelable(null);
+ final RemoteViews parentPresentation = parcel.readParcelable(null, android.widget.RemoteViews.class);
if (parentPresentation == null) return null;
final Builder builder = new Builder(parentPresentation);
diff --git a/core/java/android/service/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java
index 8539bf58da27..86341a908ad7 100644
--- a/core/java/android/service/autofill/Dataset.java
+++ b/core/java/android/service/autofill/Dataset.java
@@ -913,10 +913,10 @@ public final class Dataset implements Parcelable {
public static final @NonNull Creator<Dataset> CREATOR = new Creator<Dataset>() {
@Override
public Dataset createFromParcel(Parcel parcel) {
- final RemoteViews presentation = parcel.readParcelable(null);
- final InlinePresentation inlinePresentation = parcel.readParcelable(null);
+ final RemoteViews presentation = parcel.readParcelable(null, android.widget.RemoteViews.class);
+ final InlinePresentation inlinePresentation = parcel.readParcelable(null, android.service.autofill.InlinePresentation.class);
final InlinePresentation inlineTooltipPresentation =
- parcel.readParcelable(null);
+ parcel.readParcelable(null, android.service.autofill.InlinePresentation.class);
final ArrayList<AutofillId> ids =
parcel.createTypedArrayList(AutofillId.CREATOR);
final ArrayList<AutofillValue> values =
@@ -929,8 +929,8 @@ public final class Dataset implements Parcelable {
parcel.createTypedArrayList(InlinePresentation.CREATOR);
final ArrayList<DatasetFieldFilter> filters =
parcel.createTypedArrayList(DatasetFieldFilter.CREATOR);
- final ClipData fieldContent = parcel.readParcelable(null);
- final IntentSender authentication = parcel.readParcelable(null);
+ final ClipData fieldContent = parcel.readParcelable(null, android.content.ClipData.class);
+ final IntentSender authentication = parcel.readParcelable(null, android.content.IntentSender.class);
final String datasetId = parcel.readString();
// Always go through the builder to ensure the data ingested by
@@ -1014,7 +1014,7 @@ public final class Dataset implements Parcelable {
@Override
public DatasetFieldFilter createFromParcel(Parcel parcel) {
- return new DatasetFieldFilter((Pattern) parcel.readSerializable());
+ return new DatasetFieldFilter((Pattern) parcel.readSerializable(java.util.regex.Pattern.class.getClassLoader(), java.util.regex.Pattern.class));
}
@Override
diff --git a/core/java/android/service/autofill/DateTransformation.java b/core/java/android/service/autofill/DateTransformation.java
index 734085737159..df5ed4dace55 100644
--- a/core/java/android/service/autofill/DateTransformation.java
+++ b/core/java/android/service/autofill/DateTransformation.java
@@ -114,8 +114,8 @@ public final class DateTransformation extends InternalTransformation implements
new Parcelable.Creator<DateTransformation>() {
@Override
public DateTransformation createFromParcel(Parcel parcel) {
- return new DateTransformation(parcel.readParcelable(null),
- (DateFormat) parcel.readSerializable());
+ return new DateTransformation(parcel.readParcelable(null, android.view.autofill.AutofillId.class),
+ (DateFormat) parcel.readSerializable(android.icu.text.DateFormat.class.getClassLoader(), android.icu.text.DateFormat.class));
}
@Override
diff --git a/core/java/android/service/autofill/DateValueSanitizer.java b/core/java/android/service/autofill/DateValueSanitizer.java
index 6f7808ee181a..c7d5b79ae484 100644
--- a/core/java/android/service/autofill/DateValueSanitizer.java
+++ b/core/java/android/service/autofill/DateValueSanitizer.java
@@ -111,7 +111,7 @@ public final class DateValueSanitizer extends InternalSanitizer implements Sanit
new Parcelable.Creator<DateValueSanitizer>() {
@Override
public DateValueSanitizer createFromParcel(Parcel parcel) {
- return new DateValueSanitizer((DateFormat) parcel.readSerializable());
+ return new DateValueSanitizer((DateFormat) parcel.readSerializable(android.icu.text.DateFormat.class.getClassLoader(), android.icu.text.DateFormat.class));
}
@Override
diff --git a/core/java/android/service/autofill/FillRequest.java b/core/java/android/service/autofill/FillRequest.java
index af846b62ae2c..43bd4102ffb5 100644
--- a/core/java/android/service/autofill/FillRequest.java
+++ b/core/java/android/service/autofill/FillRequest.java
@@ -384,7 +384,7 @@ public final class FillRequest implements Parcelable {
byte flg = in.readByte();
int id = in.readInt();
List<FillContext> fillContexts = new ArrayList<>();
- in.readParcelableList(fillContexts, FillContext.class.getClassLoader());
+ in.readParcelableList(fillContexts, FillContext.class.getClassLoader(), android.service.autofill.FillContext.class);
Bundle clientState = (flg & 0x4) == 0 ? null : in.readBundle();
int flags = in.readInt();
InlineSuggestionsRequest inlineSuggestionsRequest = (flg & 0x10) == 0 ? null : (InlineSuggestionsRequest) in.readTypedObject(InlineSuggestionsRequest.CREATOR);
diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java
index 970cb1888317..d94988ebea66 100644
--- a/core/java/android/service/autofill/FillResponse.java
+++ b/core/java/android/service/autofill/FillResponse.java
@@ -834,35 +834,35 @@ public final class FillResponse implements Parcelable {
// the system obeys the contract of the builder to avoid attacks
// using specially crafted parcels.
final Builder builder = new Builder();
- final ParceledListSlice<Dataset> datasetSlice = parcel.readParcelable(null);
+ final ParceledListSlice<Dataset> datasetSlice = parcel.readParcelable(null, android.content.pm.ParceledListSlice.class);
final List<Dataset> datasets = (datasetSlice != null) ? datasetSlice.getList() : null;
final int datasetCount = (datasets != null) ? datasets.size() : 0;
for (int i = 0; i < datasetCount; i++) {
builder.addDataset(datasets.get(i));
}
- builder.setSaveInfo(parcel.readParcelable(null));
- builder.setClientState(parcel.readParcelable(null));
+ builder.setSaveInfo(parcel.readParcelable(null, android.service.autofill.SaveInfo.class));
+ builder.setClientState(parcel.readParcelable(null, android.os.Bundle.class));
// Sets authentication state.
final AutofillId[] authenticationIds = parcel.readParcelableArray(null,
AutofillId.class);
- final IntentSender authentication = parcel.readParcelable(null);
- final RemoteViews presentation = parcel.readParcelable(null);
- final InlinePresentation inlinePresentation = parcel.readParcelable(null);
- final InlinePresentation inlineTooltipPresentation = parcel.readParcelable(null);
+ final IntentSender authentication = parcel.readParcelable(null, android.content.IntentSender.class);
+ final RemoteViews presentation = parcel.readParcelable(null, android.widget.RemoteViews.class);
+ final InlinePresentation inlinePresentation = parcel.readParcelable(null, android.service.autofill.InlinePresentation.class);
+ final InlinePresentation inlineTooltipPresentation = parcel.readParcelable(null, android.service.autofill.InlinePresentation.class);
if (authenticationIds != null) {
builder.setAuthentication(authenticationIds, authentication, presentation,
inlinePresentation, inlineTooltipPresentation);
}
- final RemoteViews header = parcel.readParcelable(null);
+ final RemoteViews header = parcel.readParcelable(null, android.widget.RemoteViews.class);
if (header != null) {
builder.setHeader(header);
}
- final RemoteViews footer = parcel.readParcelable(null);
+ final RemoteViews footer = parcel.readParcelable(null, android.widget.RemoteViews.class);
if (footer != null) {
builder.setFooter(footer);
}
- final UserData userData = parcel.readParcelable(null);
+ final UserData userData = parcel.readParcelable(null, android.service.autofill.UserData.class);
if (userData != null) {
builder.setUserData(userData);
}
diff --git a/core/java/android/service/autofill/ImageTransformation.java b/core/java/android/service/autofill/ImageTransformation.java
index e3171594c39e..af82205b77b9 100644
--- a/core/java/android/service/autofill/ImageTransformation.java
+++ b/core/java/android/service/autofill/ImageTransformation.java
@@ -247,7 +247,7 @@ public final class ImageTransformation extends InternalTransformation implements
new Parcelable.Creator<ImageTransformation>() {
@Override
public ImageTransformation createFromParcel(Parcel parcel) {
- final AutofillId id = parcel.readParcelable(null);
+ final AutofillId id = parcel.readParcelable(null, android.view.autofill.AutofillId.class);
final Pattern[] regexs = (Pattern[]) parcel.readSerializable();
final int[] resIds = parcel.createIntArray();
diff --git a/core/java/android/service/autofill/NegationValidator.java b/core/java/android/service/autofill/NegationValidator.java
index d626845b3b3e..85cd981e3152 100644
--- a/core/java/android/service/autofill/NegationValidator.java
+++ b/core/java/android/service/autofill/NegationValidator.java
@@ -68,7 +68,7 @@ final class NegationValidator extends InternalValidator {
new Parcelable.Creator<NegationValidator>() {
@Override
public NegationValidator createFromParcel(Parcel parcel) {
- return new NegationValidator(parcel.readParcelable(null));
+ return new NegationValidator(parcel.readParcelable(null, android.service.autofill.InternalValidator.class));
}
@Override
diff --git a/core/java/android/service/autofill/RegexValidator.java b/core/java/android/service/autofill/RegexValidator.java
index 00c43473ce7f..4c58590ab7cf 100644
--- a/core/java/android/service/autofill/RegexValidator.java
+++ b/core/java/android/service/autofill/RegexValidator.java
@@ -96,8 +96,8 @@ public final class RegexValidator extends InternalValidator implements Validator
new Parcelable.Creator<RegexValidator>() {
@Override
public RegexValidator createFromParcel(Parcel parcel) {
- return new RegexValidator(parcel.readParcelable(null),
- (Pattern) parcel.readSerializable());
+ return new RegexValidator(parcel.readParcelable(null, android.view.autofill.AutofillId.class),
+ (Pattern) parcel.readSerializable(java.util.regex.Pattern.class.getClassLoader(), java.util.regex.Pattern.class));
}
@Override
diff --git a/core/java/android/service/autofill/SaveInfo.java b/core/java/android/service/autofill/SaveInfo.java
index 8edfde8c3914..5fe1d4f5ca5f 100644
--- a/core/java/android/service/autofill/SaveInfo.java
+++ b/core/java/android/service/autofill/SaveInfo.java
@@ -888,14 +888,14 @@ public final class SaveInfo implements Parcelable {
builder.setOptionalIds(optionalIds);
}
- builder.setNegativeAction(parcel.readInt(), parcel.readParcelable(null));
+ builder.setNegativeAction(parcel.readInt(), parcel.readParcelable(null, android.content.IntentSender.class));
builder.setPositiveAction(parcel.readInt());
builder.setDescription(parcel.readCharSequence());
- final CustomDescription customDescripton = parcel.readParcelable(null);
+ final CustomDescription customDescripton = parcel.readParcelable(null, android.service.autofill.CustomDescription.class);
if (customDescripton != null) {
builder.setCustomDescription(customDescripton);
}
- final InternalValidator validator = parcel.readParcelable(null);
+ final InternalValidator validator = parcel.readParcelable(null, android.service.autofill.InternalValidator.class);
if (validator != null) {
builder.setValidator(validator);
}
@@ -909,7 +909,7 @@ public final class SaveInfo implements Parcelable {
builder.addSanitizer(sanitizers[i], autofillIds);
}
}
- final AutofillId triggerId = parcel.readParcelable(null);
+ final AutofillId triggerId = parcel.readParcelable(null, android.view.autofill.AutofillId.class);
if (triggerId != null) {
builder.setTriggerId(triggerId);
}
diff --git a/core/java/android/service/autofill/TextValueSanitizer.java b/core/java/android/service/autofill/TextValueSanitizer.java
index 5bafa7a1ff54..46c18b23a74d 100644
--- a/core/java/android/service/autofill/TextValueSanitizer.java
+++ b/core/java/android/service/autofill/TextValueSanitizer.java
@@ -119,7 +119,7 @@ public final class TextValueSanitizer extends InternalSanitizer implements
new Parcelable.Creator<TextValueSanitizer>() {
@Override
public TextValueSanitizer createFromParcel(Parcel parcel) {
- return new TextValueSanitizer((Pattern) parcel.readSerializable(), parcel.readString());
+ return new TextValueSanitizer((Pattern) parcel.readSerializable(java.util.regex.Pattern.class.getClassLoader(), java.util.regex.Pattern.class), parcel.readString());
}
@Override
diff --git a/core/java/android/service/contentcapture/ActivityEvent.java b/core/java/android/service/contentcapture/ActivityEvent.java
index 74a735518120..d286942c74fa 100644
--- a/core/java/android/service/contentcapture/ActivityEvent.java
+++ b/core/java/android/service/contentcapture/ActivityEvent.java
@@ -149,7 +149,7 @@ public final class ActivityEvent implements Parcelable {
@Override
@NonNull
public ActivityEvent createFromParcel(@NonNull Parcel parcel) {
- final ComponentName componentName = parcel.readParcelable(null);
+ final ComponentName componentName = parcel.readParcelable(null, android.content.ComponentName.class);
final int eventType = parcel.readInt();
return new ActivityEvent(componentName, eventType);
}
diff --git a/core/java/android/service/contentcapture/SnapshotData.java b/core/java/android/service/contentcapture/SnapshotData.java
index bf469b4b3ad8..f72624d00061 100644
--- a/core/java/android/service/contentcapture/SnapshotData.java
+++ b/core/java/android/service/contentcapture/SnapshotData.java
@@ -51,8 +51,8 @@ public final class SnapshotData implements Parcelable {
SnapshotData(@NonNull Parcel parcel) {
mAssistData = parcel.readBundle();
- mAssistStructure = parcel.readParcelable(null);
- mAssistContent = parcel.readParcelable(null);
+ mAssistStructure = parcel.readParcelable(null, android.app.assist.AssistStructure.class);
+ mAssistContent = parcel.readParcelable(null, android.app.assist.AssistContent.class);
}
/**
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index 133e384dfa8f..bb1f393b99bc 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -1,4 +1,4 @@
-/**
+/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package android.service.dreams;
import android.annotation.IdRes;
@@ -57,7 +58,6 @@ import android.view.WindowManager.LayoutParams;
import android.view.accessibility.AccessibilityEvent;
import com.android.internal.util.DumpUtils;
-import com.android.internal.util.DumpUtils.Dump;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -111,7 +111,7 @@ import java.util.function.Consumer;
* <p>If specified with the {@code <meta-data>} element,
* additional information for the dream is defined using the
* {@link android.R.styleable#Dream &lt;dream&gt;} element in a separate XML file.
- * Currently, the only addtional
+ * Currently, the only additional
* information you can provide is for a settings activity that allows the user to configure
* the dream behavior. For example:</p>
* <p class="code-caption">res/xml/my_dream.xml</p>
@@ -159,7 +159,8 @@ import java.util.function.Consumer;
* </pre>
*/
public class DreamService extends Service implements Window.Callback {
- private final String TAG = DreamService.class.getSimpleName() + "[" + getClass().getSimpleName() + "]";
+ private final String mTag =
+ DreamService.class.getSimpleName() + "[" + getClass().getSimpleName() + "]";
/**
* The name of the dream manager service.
@@ -224,13 +225,13 @@ public class DreamService extends Service implements Window.Callback {
private DreamServiceWrapper mDreamServiceWrapper;
private Runnable mDispatchAfterOnAttachedToWindow;
- private OverlayConnection mOverlayConnection;
+ private final OverlayConnection mOverlayConnection;
private static class OverlayConnection implements ServiceConnection {
// Overlay set during onBind.
private IDreamOverlay mOverlay;
// A Queue of pending requests to execute on the overlay.
- private ArrayDeque<Consumer<IDreamOverlay>> mRequests;
+ private final ArrayDeque<Consumer<IDreamOverlay>> mRequests;
private boolean mBound;
@@ -292,7 +293,7 @@ public class DreamService extends Service implements Window.Callback {
}
}
- private IDreamOverlayCallback mOverlayCallback = new IDreamOverlayCallback.Stub() {
+ private final IDreamOverlayCallback mOverlayCallback = new IDreamOverlayCallback.Stub() {
@Override
public void onExitRequested() {
// Simply finish dream when exit is requested.
@@ -319,11 +320,11 @@ public class DreamService extends Service implements Window.Callback {
public boolean dispatchKeyEvent(KeyEvent event) {
// TODO: create more flexible version of mInteractive that allows use of KEYCODE_BACK
if (!mInteractive) {
- if (mDebug) Slog.v(TAG, "Waking up on keyEvent");
+ if (mDebug) Slog.v(mTag, "Waking up on keyEvent");
wakeUp();
return true;
} else if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
- if (mDebug) Slog.v(TAG, "Waking up on back key");
+ if (mDebug) Slog.v(mTag, "Waking up on back key");
wakeUp();
return true;
}
@@ -334,7 +335,7 @@ public class DreamService extends Service implements Window.Callback {
@Override
public boolean dispatchKeyShortcutEvent(KeyEvent event) {
if (!mInteractive) {
- if (mDebug) Slog.v(TAG, "Waking up on keyShortcutEvent");
+ if (mDebug) Slog.v(mTag, "Waking up on keyShortcutEvent");
wakeUp();
return true;
}
@@ -347,7 +348,7 @@ public class DreamService extends Service implements Window.Callback {
// TODO: create more flexible version of mInteractive that allows clicks
// but finish()es on any other kind of activity
if (!mInteractive && event.getActionMasked() == MotionEvent.ACTION_UP) {
- if (mDebug) Slog.v(TAG, "Waking up on touchEvent");
+ if (mDebug) Slog.v(mTag, "Waking up on touchEvent");
wakeUp();
return true;
}
@@ -358,7 +359,7 @@ public class DreamService extends Service implements Window.Callback {
@Override
public boolean dispatchTrackballEvent(MotionEvent event) {
if (!mInteractive) {
- if (mDebug) Slog.v(TAG, "Waking up on trackballEvent");
+ if (mDebug) Slog.v(mTag, "Waking up on trackballEvent");
wakeUp();
return true;
}
@@ -369,7 +370,7 @@ public class DreamService extends Service implements Window.Callback {
@Override
public boolean dispatchGenericMotionEvent(MotionEvent event) {
if (!mInteractive) {
- if (mDebug) Slog.v(TAG, "Waking up on genericMotionEvent");
+ if (mDebug) Slog.v(mTag, "Waking up on genericMotionEvent");
wakeUp();
return true;
}
@@ -624,7 +625,7 @@ public class DreamService extends Service implements Window.Callback {
}
/**
- * Returns whether or not this dream is interactive. Defaults to false.
+ * Returns whether this dream is interactive. Defaults to false.
*
* @see #setInteractive(boolean)
*/
@@ -648,7 +649,7 @@ public class DreamService extends Service implements Window.Callback {
}
/**
- * Returns whether or not this dream is in fullscreen mode. Defaults to false.
+ * Returns whether this dream is in fullscreen mode. Defaults to false.
*
* @see #setFullscreen(boolean)
*/
@@ -670,7 +671,7 @@ public class DreamService extends Service implements Window.Callback {
}
/**
- * Returns whether or not this dream keeps the screen bright while dreaming.
+ * Returns whether this dream keeps the screen bright while dreaming.
* Defaults to false, allowing the screen to dim if necessary.
*
* @see #setScreenBright(boolean)
@@ -680,7 +681,7 @@ public class DreamService extends Service implements Window.Callback {
}
/**
- * Marks this dream as windowless. Only available to doze dreams.
+ * Marks this dream as windowless. Only available to doze dreams.
*
* @hide
*
@@ -690,7 +691,7 @@ public class DreamService extends Service implements Window.Callback {
}
/**
- * Returns whether or not this dream is windowless. Only available to doze dreams.
+ * Returns whether this dream is windowless. Only available to doze dreams.
*
* @hide
*/
@@ -717,12 +718,12 @@ public class DreamService extends Service implements Window.Callback {
* Starts dozing, entering a deep dreamy sleep.
* <p>
* Dozing enables the system to conserve power while the user is not actively interacting
- * with the device. While dozing, the display will remain on in a low-power state
+ * with the device. While dozing, the display will remain on in a low-power state
* and will continue to show its previous contents but the application processor and
* other system components will be allowed to suspend when possible.
* </p><p>
* While the application processor is suspended, the dream may stop executing code
- * for long periods of time. Prior to being suspended, the dream may schedule periodic
+ * for long periods of time. Prior to being suspended, the dream may schedule periodic
* wake-ups to render new content by scheduling an alarm with the {@link AlarmManager}.
* The dream may also keep the CPU awake by acquiring a
* {@link android.os.PowerManager#PARTIAL_WAKE_LOCK partial wake lock} when necessary.
@@ -731,7 +732,7 @@ public class DreamService extends Service implements Window.Callback {
* awake for very long.
* </p><p>
* It is a good idea to call this method some time after the dream's entry animation
- * has completed and the dream is ready to doze. It is important to completely
+ * has completed and the dream is ready to doze. It is important to completely
* finish all of the work needed before dozing since the application processor may
* be suspended at any moment once this method is called unless other wake locks
* are being held.
@@ -752,7 +753,7 @@ public class DreamService extends Service implements Window.Callback {
private void updateDoze() {
if (mDreamToken == null) {
- Slog.w(TAG, "Updating doze without a dream token.");
+ Slog.w(mTag, "Updating doze without a dream token.");
return;
}
@@ -768,7 +769,7 @@ public class DreamService extends Service implements Window.Callback {
/**
* Stops dozing, returns to active dreaming.
* <p>
- * This method reverses the effect of {@link #startDozing}. From this moment onward,
+ * This method reverses the effect of {@link #startDozing}. From this moment onward,
* the application processor will be kept awake as long as the dream is running
* or until the dream starts dozing again.
* </p>
@@ -790,12 +791,11 @@ public class DreamService extends Service implements Window.Callback {
/**
* Returns true if the dream will allow the system to enter a low-power state while
- * it is running without actually turning off the screen. Defaults to false,
+ * it is running without actually turning off the screen. Defaults to false,
* keeping the application processor awake while the dream is running.
*
* @return True if the dream is dozing.
*
- * @see #setDozing(boolean)
* @hide For use by system UI components only.
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
@@ -822,7 +822,7 @@ public class DreamService extends Service implements Window.Callback {
* Sets the screen state to use while dozing.
* <p>
* The value of this property determines the power state of the primary display
- * once {@link #startDozing} has been called. The default value is
+ * once {@link #startDozing} has been called. The default value is
* {@link Display#STATE_UNKNOWN} which lets the system decide.
* The dream may set a different state before starting to doze and may
* perform transitions between states while dozing to conserve power and
@@ -836,7 +836,7 @@ public class DreamService extends Service implements Window.Callback {
* If not using Sidekick, it is recommended that the state be set to
* {@link Display#STATE_DOZE_SUSPEND} once the dream has completely
* finished drawing and before it releases its wakelock
- * to allow the display hardware to be fully suspended. While suspended,
+ * to allow the display hardware to be fully suspended. While suspended,
* the display will preserve its on-screen contents.
* </p><p>
* If the doze suspend state is used, the dream must make sure to set the mode back
@@ -844,7 +844,7 @@ public class DreamService extends Service implements Window.Callback {
* since the display updates may be ignored and not seen by the user otherwise.
* </p><p>
* The set of available display power states and their behavior while dozing is
- * hardware dependent and may vary across devices. The dream may therefore
+ * hardware dependent and may vary across devices. The dream may therefore
* need to be modified or configured to correctly support the hardware.
* </p>
*
@@ -883,19 +883,19 @@ public class DreamService extends Service implements Window.Callback {
* Sets the screen brightness to use while dozing.
* <p>
* The value of this property determines the power state of the primary display
- * once {@link #startDozing} has been called. The default value is
+ * once {@link #startDozing} has been called. The default value is
* {@link PowerManager#BRIGHTNESS_DEFAULT} which lets the system decide.
* The dream may set a different brightness before starting to doze and may adjust
* the brightness while dozing to conserve power and achieve various effects.
* </p><p>
* Note that dream may specify any brightness in the full 0-255 range, including
* values that are less than the minimum value for manual screen brightness
- * adjustments by the user. In particular, the value may be set to 0 which may
+ * adjustments by the user. In particular, the value may be set to 0 which may
* turn off the backlight entirely while still leaving the screen on although
* this behavior is device dependent and not guaranteed.
* </p><p>
* The available range of display brightness values and their behavior while dozing is
- * hardware dependent and may vary across devices. The dream may therefore
+ * hardware dependent and may vary across devices. The dream may therefore
* need to be modified or configured to correctly support the hardware.
* </p>
*
@@ -922,7 +922,7 @@ public class DreamService extends Service implements Window.Callback {
*/
@Override
public void onCreate() {
- if (mDebug) Slog.v(TAG, "onCreate()");
+ if (mDebug) Slog.v(mTag, "onCreate()");
super.onCreate();
}
@@ -930,7 +930,7 @@ public class DreamService extends Service implements Window.Callback {
* Called when the dream's window has been created and is visible and animation may now begin.
*/
public void onDreamingStarted() {
- if (mDebug) Slog.v(TAG, "onDreamingStarted()");
+ if (mDebug) Slog.v(mTag, "onDreamingStarted()");
// hook for subclasses
}
@@ -939,7 +939,7 @@ public class DreamService extends Service implements Window.Callback {
* before the window has been removed.
*/
public void onDreamingStopped() {
- if (mDebug) Slog.v(TAG, "onDreamingStopped()");
+ if (mDebug) Slog.v(mTag, "onDreamingStopped()");
// hook for subclasses
}
@@ -947,11 +947,11 @@ public class DreamService extends Service implements Window.Callback {
* Called when the dream is being asked to stop itself and wake.
* <p>
* The default implementation simply calls {@link #finish} which ends the dream
- * immediately. Subclasses may override this function to perform a smooth exit
+ * immediately. Subclasses may override this function to perform a smooth exit
* transition then call {@link #finish} afterwards.
* </p><p>
* Note that the dream will only be given a short period of time (currently about
- * five seconds) to wake up. If the dream does not finish itself in a timely manner
+ * five seconds) to wake up. If the dream does not finish itself in a timely manner
* then the system will forcibly finish it once the time allowance is up.
* </p>
*/
@@ -962,7 +962,7 @@ public class DreamService extends Service implements Window.Callback {
/** {@inheritDoc} */
@Override
public final IBinder onBind(Intent intent) {
- if (mDebug) Slog.v(TAG, "onBind() intent = " + intent);
+ if (mDebug) Slog.v(mTag, "onBind() intent = " + intent);
mDreamServiceWrapper = new DreamServiceWrapper();
// Connect to the overlay service if present.
@@ -981,7 +981,7 @@ public class DreamService extends Service implements Window.Callback {
* </p>
*/
public final void finish() {
- if (mDebug) Slog.v(TAG, "finish(): mFinished=" + mFinished);
+ if (mDebug) Slog.v(mTag, "finish(): mFinished=" + mFinished);
Activity activity = mActivity;
if (activity != null) {
@@ -998,7 +998,7 @@ public class DreamService extends Service implements Window.Callback {
mFinished = true;
if (mDreamToken == null) {
- Slog.w(TAG, "Finish was called before the dream was attached.");
+ Slog.w(mTag, "Finish was called before the dream was attached.");
stopSelf();
return;
}
@@ -1026,8 +1026,10 @@ public class DreamService extends Service implements Window.Callback {
}
private void wakeUp(boolean fromSystem) {
- if (mDebug) Slog.v(TAG, "wakeUp(): fromSystem=" + fromSystem
- + ", mWaking=" + mWaking + ", mFinished=" + mFinished);
+ if (mDebug) {
+ Slog.v(mTag, "wakeUp(): fromSystem=" + fromSystem + ", mWaking=" + mWaking
+ + ", mFinished=" + mFinished);
+ }
if (!mWaking && !mFinished) {
mWaking = true;
@@ -1052,7 +1054,7 @@ public class DreamService extends Service implements Window.Callback {
// it we were finishing immediately.
if (!fromSystem && !mFinished) {
if (mActivity == null) {
- Slog.w(TAG, "WakeUp was called before the dream was attached.");
+ Slog.w(mTag, "WakeUp was called before the dream was attached.");
} else {
try {
mDreamManager.finishSelf(mDreamToken, false /*immediate*/);
@@ -1067,7 +1069,7 @@ public class DreamService extends Service implements Window.Callback {
/** {@inheritDoc} */
@Override
public void onDestroy() {
- if (mDebug) Slog.v(TAG, "onDestroy()");
+ if (mDebug) Slog.v(mTag, "onDestroy()");
// hook for subclasses
// Just in case destroy came in before detach, let's take care of that now
@@ -1083,9 +1085,9 @@ public class DreamService extends Service implements Window.Callback {
*
* Must run on mHandler.
*/
- private final void detach() {
+ private void detach() {
if (mStarted) {
- if (mDebug) Slog.v(TAG, "detach(): Calling onDreamingStopped()");
+ if (mDebug) Slog.v(mTag, "detach(): Calling onDreamingStopped()");
mStarted = false;
onDreamingStopped();
}
@@ -1110,12 +1112,12 @@ public class DreamService extends Service implements Window.Callback {
*/
private void attach(IBinder dreamToken, boolean canDoze, IRemoteCallback started) {
if (mDreamToken != null) {
- Slog.e(TAG, "attach() called when dream with token=" + mDreamToken
+ Slog.e(mTag, "attach() called when dream with token=" + mDreamToken
+ " already attached");
return;
}
if (mFinished || mWaking) {
- Slog.w(TAG, "attach() called after dream already finished");
+ Slog.w(mTag, "attach() called after dream already finished");
try {
mDreamManager.finishSelf(dreamToken, true /*immediate*/);
} catch (RemoteException ex) {
@@ -1158,10 +1160,9 @@ public class DreamService extends Service implements Window.Callback {
try {
if (!ActivityTaskManager.getService().startDreamActivity(i)) {
detach();
- return;
}
} catch (RemoteException e) {
- Log.w(TAG, "Could not connect to activity task manager to start dream activity");
+ Log.w(mTag, "Could not connect to activity task manager to start dream activity");
e.rethrowFromSystemServer();
}
} else {
@@ -1191,7 +1192,7 @@ public class DreamService extends Service implements Window.Callback {
// along well. Dreams usually don't need such bars anyways, so disable them by default.
mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
- // Hide all insets when the dream is showing
+ // Hide all insets when the dream is showing
mWindow.getDecorView().getWindowInsetsController().hide(WindowInsets.Type.systemBars());
mWindow.setDecorFitsSystemWindows(false);
@@ -1207,7 +1208,7 @@ public class DreamService extends Service implements Window.Callback {
try {
overlay.startDream(mWindow.getAttributes(), mOverlayCallback);
} catch (RemoteException e) {
- Log.e(TAG, "could not send window attributes:" + e);
+ Log.e(mTag, "could not send window attributes:" + e);
}
});
}
@@ -1243,17 +1244,12 @@ public class DreamService extends Service implements Window.Callback {
@Override
protected void dump(final FileDescriptor fd, PrintWriter pw, final String[] args) {
- DumpUtils.dumpAsync(mHandler, new Dump() {
- @Override
- public void dump(PrintWriter pw, String prefix) {
- dumpOnHandler(fd, pw, args);
- }
- }, pw, "", 1000);
+ DumpUtils.dumpAsync(mHandler, (pw1, prefix) -> dumpOnHandler(fd, pw1, args), pw, "", 1000);
}
/** @hide */
protected void dumpOnHandler(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.print(TAG + ": ");
+ pw.print(mTag + ": ");
if (mFinished) {
pw.println("stopped");
} else {
diff --git a/core/java/android/service/games/CreateGameSessionResult.aidl b/core/java/android/service/games/CreateGameSessionResult.aidl
new file mode 100644
index 000000000000..b7c5e1602891
--- /dev/null
+++ b/core/java/android/service/games/CreateGameSessionResult.aidl
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.games;
+
+
+/**
+ * @hide
+ */
+parcelable CreateGameSessionResult;
diff --git a/core/java/android/service/games/CreateGameSessionResult.java b/core/java/android/service/games/CreateGameSessionResult.java
new file mode 100644
index 000000000000..8448b0f433b2
--- /dev/null
+++ b/core/java/android/service/games/CreateGameSessionResult.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.games;
+
+
+import android.annotation.Hide;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.SurfaceControlViewHost;
+
+/**
+ * Internal result object that contains the successful creation of a game session.
+ *
+ * @see IGameSessionService#create(CreateGameSessionRequest, GameSessionViewHostConfiguration,
+ * com.android.internal.infra.AndroidFuture)
+ * @hide
+ */
+@Hide
+public final class CreateGameSessionResult implements Parcelable {
+
+ @NonNull
+ public static final Parcelable.Creator<CreateGameSessionResult> CREATOR =
+ new Parcelable.Creator<CreateGameSessionResult>() {
+ @Override
+ public CreateGameSessionResult createFromParcel(Parcel source) {
+ return new CreateGameSessionResult(
+ IGameSession.Stub.asInterface(source.readStrongBinder()),
+ source.readParcelable(
+ SurfaceControlViewHost.SurfacePackage.class.getClassLoader(),
+ SurfaceControlViewHost.SurfacePackage.class));
+ }
+
+ @Override
+ public CreateGameSessionResult[] newArray(int size) {
+ return new CreateGameSessionResult[0];
+ }
+ };
+
+ private final IGameSession mGameSession;
+ private final SurfaceControlViewHost.SurfacePackage mSurfacePackage;
+
+ public CreateGameSessionResult(
+ @NonNull IGameSession gameSession,
+ @NonNull SurfaceControlViewHost.SurfacePackage surfacePackage) {
+ mGameSession = gameSession;
+ mSurfacePackage = surfacePackage;
+ }
+
+ @NonNull
+ public IGameSession getGameSession() {
+ return mGameSession;
+ }
+
+ @NonNull
+ public SurfaceControlViewHost.SurfacePackage getSurfacePackage() {
+ return mSurfacePackage;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeStrongBinder(mGameSession.asBinder());
+ dest.writeParcelable(mSurfacePackage, flags);
+ }
+}
diff --git a/core/java/android/service/games/GameScreenshotResult.java b/core/java/android/service/games/GameScreenshotResult.java
new file mode 100644
index 000000000000..ae76e08c7971
--- /dev/null
+++ b/core/java/android/service/games/GameScreenshotResult.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.games;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.Bitmap;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Result object for calls to {@link IGameSessionController#takeScreenshot}.
+ *
+ * It includes a status (see {@link #getStatus}) and, if the status is
+ * {@link #GAME_SCREENSHOT_SUCCESS} an {@link android.graphics.Bitmap} result (see {@link
+ * #getBitmap}).
+ *
+ * @hide
+ */
+public final class GameScreenshotResult implements Parcelable {
+
+ /**
+ * The status of a call to {@link IGameSessionController#takeScreenshot} will be represented by
+ * one of these values.
+ *
+ * @hide
+ */
+ @IntDef(flag = false, prefix = {"GAME_SCREENSHOT_"}, value = {
+ GAME_SCREENSHOT_SUCCESS, // 0
+ GAME_SCREENSHOT_ERROR_INTERNAL_ERROR, // 1
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface GameScreenshotStatus {
+ }
+
+ /**
+ * Indicates that the result of a call to {@link IGameSessionController#takeScreenshot} was
+ * successful and an {@link android.graphics.Bitmap} result should be available by calling
+ * {@link #getBitmap}.
+ *
+ * @hide
+ */
+ public static final int GAME_SCREENSHOT_SUCCESS = 0;
+
+ /**
+ * Indicates that the result of a call to {@link IGameSessionController#takeScreenshot} failed
+ * due to an internal error.
+ *
+ * This error may occur if the device is not in a suitable state for a screenshot to be taken
+ * (e.g., the screen is off) or if the game task is not in a suitable state for a screenshot
+ * to be taken (e.g., the task is not visible). To make sure that the device and game are
+ * in a suitable state, the caller can monitor the lifecycle methods for the {@link
+ * GameSession} to make sure that the game task is focused. If the conditions are met, then the
+ * caller may try again immediately.
+ *
+ * @hide
+ */
+ public static final int GAME_SCREENSHOT_ERROR_INTERNAL_ERROR = 1;
+
+ @NonNull
+ public static final Parcelable.Creator<GameScreenshotResult> CREATOR =
+ new Parcelable.Creator<GameScreenshotResult>() {
+ @Override
+ public GameScreenshotResult createFromParcel(Parcel source) {
+ return new GameScreenshotResult(
+ source.readInt(),
+ source.readParcelable(null, Bitmap.class));
+ }
+
+ @Override
+ public GameScreenshotResult[] newArray(int size) {
+ return new GameScreenshotResult[0];
+ }
+ };
+
+ @GameScreenshotStatus
+ private final int mStatus;
+
+ @Nullable
+ private final Bitmap mBitmap;
+
+ /**
+ * Creates a successful {@link GameScreenshotResult} with the provided bitmap.
+ */
+ public static GameScreenshotResult createSuccessResult(@NonNull Bitmap bitmap) {
+ return new GameScreenshotResult(GAME_SCREENSHOT_SUCCESS, bitmap);
+ }
+
+ /**
+ * Creates a failed {@link GameScreenshotResult} with an
+ * {@link #GAME_SCREENSHOT_ERROR_INTERNAL_ERROR} status.
+ */
+ public static GameScreenshotResult createInternalErrorResult() {
+ return new GameScreenshotResult(GAME_SCREENSHOT_ERROR_INTERNAL_ERROR, null);
+ }
+
+ private GameScreenshotResult(@GameScreenshotStatus int status, @Nullable Bitmap bitmap) {
+ this.mStatus = status;
+ this.mBitmap = bitmap;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mStatus);
+ dest.writeParcelable(mBitmap, flags);
+ }
+
+ @GameScreenshotStatus
+ public int getStatus() {
+ return mStatus;
+ }
+
+ /**
+ * Gets the {@link Bitmap} result from a successful screenshot attempt.
+ *
+ * @return The bitmap.
+ * @throws IllegalStateException if this method is called when {@link #getStatus} does not
+ * return {@link #GAME_SCREENSHOT_SUCCESS}.
+ */
+ @NonNull
+ public Bitmap getBitmap() {
+ if (mBitmap == null) {
+ throw new IllegalStateException("Bitmap not available for failed screenshot result");
+ }
+ return mBitmap;
+ }
+
+ @Override
+ public String toString() {
+ return "GameScreenshotResult{"
+ + "mStatus="
+ + mStatus
+ + ", has bitmap='"
+ + mBitmap != null ? "yes" : "no"
+ + "\'}";
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+
+ if (!(o instanceof GameScreenshotResult)) {
+ return false;
+ }
+
+ GameScreenshotResult that = (GameScreenshotResult) o;
+ return mStatus == that.mStatus
+ && Objects.equals(mBitmap, that.mBitmap);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mStatus, mBitmap);
+ }
+}
diff --git a/core/java/android/service/games/GameSession.java b/core/java/android/service/games/GameSession.java
index 0ff08c08932b..cb5c19b72bd0 100644
--- a/core/java/android/service/games/GameSession.java
+++ b/core/java/android/service/games/GameSession.java
@@ -16,11 +16,32 @@
package android.service.games;
+import android.annotation.Hide;
+import android.annotation.IntDef;
+import android.annotation.MainThread;
+import android.annotation.NonNull;
import android.annotation.SystemApi;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.Bitmap;
+import android.graphics.Rect;
import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.util.Slog;
+import android.view.SurfaceControlViewHost;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.infra.AndroidFuture;
import com.android.internal.util.function.pooled.PooledLambda;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.Executor;
+
/**
* An active game session, providing a facility for the implementation to interact with the game.
*
@@ -28,25 +49,177 @@ import com.android.internal.util.function.pooled.PooledLambda;
* which is then returned when a game session is created via
* {@link GameSessionService#onNewSession(CreateGameSessionRequest)}.
*
+ * This class exposes various lifecycle methods which are guaranteed to be called in the following
+ * fashion:
+ *
+ * {@link #onCreate()}: Will always be the first lifecycle method to be called, once the game
+ * session is created.
+ *
+ * {@link #onGameTaskFocusChanged(boolean)}: Will be called after {@link #onCreate()} with
+ * focused=true when the game task first comes into focus (if it does). If the game task is focused
+ * when the game session is created, this method will be called immediately after
+ * {@link #onCreate()} with focused=true. After this method is called with focused=true, it will be
+ * called again with focused=false when the task goes out of focus. If this method is ever called
+ * with focused=true, it is guaranteed to be called again with focused=false before
+ * {@link #onDestroy()} is called. If the game task never comes into focus during the session
+ * lifetime, this method will never be called.
+ *
+ * {@link #onDestroy()}: Will always be called after {@link #onCreate()}. If the game task ever
+ * comes into focus before the game session is destroyed, then this method will be called after one
+ * or more pairs of calls to {@link #onGameTaskFocusChanged(boolean)}.
+ *
* @hide
*/
@SystemApi
public abstract class GameSession {
+ private static final String TAG = "GameSession";
+ private static final boolean DEBUG = false;
final IGameSession mInterface = new IGameSession.Stub() {
@Override
- public void destroy() {
+ public void onDestroyed() {
Handler.getMain().executeOrSendMessage(PooledLambda.obtainMessage(
GameSession::doDestroy, GameSession.this));
}
+
+ @Override
+ public void onTaskFocusChanged(boolean focused) {
+ Handler.getMain().executeOrSendMessage(PooledLambda.obtainMessage(
+ GameSession::moveToState, GameSession.this,
+ focused ? LifecycleState.TASK_FOCUSED : LifecycleState.TASK_UNFOCUSED));
+ }
};
+ /**
+ * @hide
+ */
+ @VisibleForTesting
+ public enum LifecycleState {
+ // Initial state; may transition to CREATED.
+ INITIALIZED,
+ // May transition to TASK_FOCUSED or DESTROYED.
+ CREATED,
+ // May transition to TASK_UNFOCUSED.
+ TASK_FOCUSED,
+ // May transition to TASK_FOCUSED or DESTROYED.
+ TASK_UNFOCUSED,
+ // May not transition once reached.
+ DESTROYED
+ }
+
+ private LifecycleState mLifecycleState = LifecycleState.INITIALIZED;
+ private IGameSessionController mGameSessionController;
+ private int mTaskId;
+ private GameSessionRootView mGameSessionRootView;
+ private SurfaceControlViewHost mSurfaceControlViewHost;
+
+ /**
+ * @hide
+ */
+ @VisibleForTesting
+ public void attach(
+ IGameSessionController gameSessionController,
+ int taskId,
+ @NonNull Context context,
+ @NonNull SurfaceControlViewHost surfaceControlViewHost,
+ int widthPx,
+ int heightPx) {
+ mGameSessionController = gameSessionController;
+ mTaskId = taskId;
+ mSurfaceControlViewHost = surfaceControlViewHost;
+ mGameSessionRootView = new GameSessionRootView(context, mSurfaceControlViewHost);
+ surfaceControlViewHost.setView(mGameSessionRootView, widthPx, heightPx);
+ }
+
+ @Hide
void doCreate() {
- onCreate();
+ moveToState(LifecycleState.CREATED);
}
+ @Hide
void doDestroy() {
- onDestroy();
+ mSurfaceControlViewHost.release();
+ moveToState(LifecycleState.DESTROYED);
+ }
+
+ /**
+ * @hide
+ */
+ @VisibleForTesting
+ @MainThread
+ public void moveToState(LifecycleState newLifecycleState) {
+ if (DEBUG) {
+ Slog.d(TAG, "moveToState: " + mLifecycleState + " -> " + newLifecycleState);
+ }
+
+ if (Looper.myLooper() != Looper.getMainLooper()) {
+ throw new RuntimeException("moveToState should be used only from the main thread");
+ }
+
+ if (mLifecycleState == newLifecycleState) {
+ // Nothing to do.
+ return;
+ }
+
+ switch (mLifecycleState) {
+ case INITIALIZED:
+ if (newLifecycleState == LifecycleState.CREATED) {
+ onCreate();
+ } else if (newLifecycleState == LifecycleState.DESTROYED) {
+ onCreate();
+ onDestroy();
+ } else {
+ if (DEBUG) {
+ Slog.d(TAG, "Ignoring moveToState: INITIALIZED -> " + newLifecycleState);
+ }
+ return;
+ }
+ break;
+ case CREATED:
+ if (newLifecycleState == LifecycleState.TASK_FOCUSED) {
+ onGameTaskFocusChanged(/*focused=*/ true);
+ } else if (newLifecycleState == LifecycleState.DESTROYED) {
+ onDestroy();
+ } else {
+ if (DEBUG) {
+ Slog.d(TAG, "Ignoring moveToState: CREATED -> " + newLifecycleState);
+ }
+ return;
+ }
+ break;
+ case TASK_FOCUSED:
+ if (newLifecycleState == LifecycleState.TASK_UNFOCUSED) {
+ onGameTaskFocusChanged(/*focused=*/ false);
+ } else if (newLifecycleState == LifecycleState.DESTROYED) {
+ onGameTaskFocusChanged(/*focused=*/ false);
+ onDestroy();
+ } else {
+ if (DEBUG) {
+ Slog.d(TAG, "Ignoring moveToState: TASK_FOCUSED -> " + newLifecycleState);
+ }
+ return;
+ }
+ break;
+ case TASK_UNFOCUSED:
+ if (newLifecycleState == LifecycleState.TASK_FOCUSED) {
+ onGameTaskFocusChanged(/*focused=*/ true);
+ } else if (newLifecycleState == LifecycleState.DESTROYED) {
+ onDestroy();
+ } else {
+ if (DEBUG) {
+ Slog.d(TAG, "Ignoring moveToState: TASK_UNFOCUSED -> " + newLifecycleState);
+ }
+ return;
+ }
+ break;
+ case DESTROYED:
+ if (DEBUG) {
+ Slog.d(TAG, "Ignoring moveToState: DESTROYED -> " + newLifecycleState);
+ }
+ return;
+ }
+
+ mLifecycleState = newLifecycleState;
}
/**
@@ -54,12 +227,173 @@ public abstract class GameSession {
*
* This should be used perform any setup required now that the game session is created.
*/
- public void onCreate() {}
+ public void onCreate() {
+ }
/**
- * Finalizer called when the game session is ending.
+ * Finalizer called when the game session is ending. This method will always be called after a
+ * call to {@link #onCreate()}. If the game task is ever in focus, this method will be called
+ * after one or more pairs of calls to {@link #onGameTaskFocusChanged(boolean)}.
*
* This should be used to perform any cleanup before the game session is destroyed.
*/
- public void onDestroy() {}
+ public void onDestroy() {
+ }
+
+ /**
+ * Called when the game task for this session is or unfocused. The initial call to this method
+ * will always come after a call to {@link #onCreate()} with focused=true (when the game task
+ * first comes into focus after the session is created, or immediately after the session is
+ * created if the game task is already focused).
+ *
+ * This should be used to perform any setup required when the game task comes into focus or any
+ * cleanup that is required when the game task goes out of focus.
+ *
+ * @param focused True if the game task is focused, false if the game task is unfocused.
+ */
+ public void onGameTaskFocusChanged(boolean focused) {}
+
+ /**
+ * Sets the task overlay content to an explicit view. This view is placed directly into the game
+ * session's task overlay view hierarchy. It can itself be a complex view hierarchy. The size
+ * the task overlay view will always match the dimensions of the associated task's window. The
+ * {@code View} may not be cleared once set, but may be replaced by invoking
+ * {@link #setTaskOverlayView(View, ViewGroup.LayoutParams)} again.
+ *
+ * @param view The desired content to display.
+ * @param layoutParams Layout parameters for the view.
+ */
+ public void setTaskOverlayView(
+ @NonNull View view,
+ @NonNull ViewGroup.LayoutParams layoutParams) {
+ mGameSessionRootView.removeAllViews();
+ mGameSessionRootView.addView(view, layoutParams);
+ }
+
+ /**
+ * Root view of the {@link SurfaceControlViewHost} associated with the {@link GameSession}
+ * instance. It is responsible for observing changes in the size of the window and resizing
+ * itself to match.
+ */
+ private static final class GameSessionRootView extends FrameLayout {
+ private final SurfaceControlViewHost mSurfaceControlViewHost;
+
+ GameSessionRootView(@NonNull Context context,
+ SurfaceControlViewHost surfaceControlViewHost) {
+ super(context);
+ mSurfaceControlViewHost = surfaceControlViewHost;
+ }
+
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+
+ // TODO(b/204504596): Investigate skipping the relayout in cases where the size has
+ // not changed.
+ Rect bounds = newConfig.windowConfiguration.getBounds();
+ mSurfaceControlViewHost.relayout(bounds.width(), bounds.height());
+ }
+ }
+
+ /**
+ * Interface for returning screenshot outcome from calls to {@link #takeScreenshot}.
+ */
+ public interface ScreenshotCallback {
+
+ /**
+ * The status of a failed screenshot attempt provided by {@link #onFailure}.
+ *
+ * @hide
+ */
+ @IntDef(flag = false, prefix = {"ERROR_TAKE_SCREENSHOT_"}, value = {
+ ERROR_TAKE_SCREENSHOT_INTERNAL_ERROR, // 0
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface ScreenshotFailureStatus {
+ }
+
+ /**
+ * An error code indicating that an internal error occurred when attempting to take a
+ * screenshot of the game task. If this code is returned, the caller should verify that the
+ * conditions for taking a screenshot are met (device screen is on and the game task is
+ * visible). To do so, the caller can monitor the lifecycle methods for this session to
+ * make sure that the game task is focused. If the conditions are met, then the caller may
+ * try again immediately.
+ */
+ int ERROR_TAKE_SCREENSHOT_INTERNAL_ERROR = 0;
+
+ /**
+ * Called when taking the screenshot failed.
+ * @param statusCode Indicates the reason for failure.
+ */
+ void onFailure(@ScreenshotFailureStatus int statusCode);
+
+ /**
+ * Called when taking the screenshot succeeded.
+ * @param bitmap The screenshot.
+ */
+ void onSuccess(@NonNull Bitmap bitmap);
+ }
+
+ /**
+ * Takes a screenshot of the associated game. For this call to succeed, the device screen
+ * must be turned on and the game task must be visible.
+ *
+ * If the callback is called with {@link ScreenshotCallback#onSuccess}, the provided {@link
+ * Bitmap} may be used.
+ *
+ * If the callback is called with {@link ScreenshotCallback#onFailure}, the provided status
+ * code should be checked.
+ *
+ * If the status code is {@link ScreenshotCallback#ERROR_TAKE_SCREENSHOT_INTERNAL_ERROR},
+ * then the caller should verify that the conditions for calling this method are met (device
+ * screen is on and the game task is visible). To do so, the caller can monitor the lifecycle
+ * methods for this session to make sure that the game task is focused. If the conditions are
+ * met, then the caller may try again immediately.
+ *
+ * @param executor Executor on which to run the callback.
+ * @param callback The callback invoked when taking screenshot has succeeded
+ * or failed.
+ * @throws IllegalStateException if this method is called prior to {@link #onCreate}.
+ */
+ public void takeScreenshot(@NonNull Executor executor, @NonNull ScreenshotCallback callback) {
+ if (mGameSessionController == null) {
+ throw new IllegalStateException("Can not call before onCreate()");
+ }
+
+ AndroidFuture<GameScreenshotResult> takeScreenshotResult =
+ new AndroidFuture<GameScreenshotResult>().whenCompleteAsync((result, error) -> {
+ handleScreenshotResult(callback, result, error);
+ }, executor);
+
+ try {
+ mGameSessionController.takeScreenshot(mTaskId, takeScreenshotResult);
+ } catch (RemoteException ex) {
+ takeScreenshotResult.completeExceptionally(ex);
+ }
+ }
+
+ private void handleScreenshotResult(
+ @NonNull ScreenshotCallback callback,
+ @NonNull GameScreenshotResult result,
+ @NonNull Throwable error) {
+ if (error != null) {
+ Slog.w(TAG, error.getMessage(), error.getCause());
+ callback.onFailure(
+ ScreenshotCallback.ERROR_TAKE_SCREENSHOT_INTERNAL_ERROR);
+ return;
+ }
+
+ @GameScreenshotResult.GameScreenshotStatus int status = result.getStatus();
+ switch (status) {
+ case GameScreenshotResult.GAME_SCREENSHOT_SUCCESS:
+ callback.onSuccess(result.getBitmap());
+ break;
+ case GameScreenshotResult.GAME_SCREENSHOT_ERROR_INTERNAL_ERROR:
+ Slog.w(TAG, "Error taking screenshot");
+ callback.onFailure(
+ ScreenshotCallback.ERROR_TAKE_SCREENSHOT_INTERNAL_ERROR);
+ break;
+ }
+ }
}
diff --git a/core/java/android/service/games/GameSessionService.java b/core/java/android/service/games/GameSessionService.java
index c1a3eb5286c4..df5bad5c53b2 100644
--- a/core/java/android/service/games/GameSessionService.java
+++ b/core/java/android/service/games/GameSessionService.java
@@ -22,8 +22,12 @@ import android.annotation.SdkConstant;
import android.annotation.SystemApi;
import android.app.Service;
import android.content.Intent;
+import android.hardware.display.DisplayManager;
+import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
+import android.view.Display;
+import android.view.SurfaceControlViewHost;
import com.android.internal.infra.AndroidFuture;
import com.android.internal.util.function.pooled.PooledLambda;
@@ -48,8 +52,6 @@ import java.util.Objects;
*/
@SystemApi
public abstract class GameSessionService extends Service {
- private static final String TAG = "GameSessionService";
-
/**
* The {@link Intent} action used when binding to the service.
* To be supported, the service must require the
@@ -62,15 +64,28 @@ public abstract class GameSessionService extends Service {
private final IGameSessionService mInterface = new IGameSessionService.Stub() {
@Override
- public void create(CreateGameSessionRequest createGameSessionRequest,
+ public void create(
+ IGameSessionController gameSessionController,
+ CreateGameSessionRequest createGameSessionRequest,
+ GameSessionViewHostConfiguration gameSessionViewHostConfiguration,
AndroidFuture gameSessionFuture) {
Handler.getMain().post(PooledLambda.obtainRunnable(
GameSessionService::doCreate, GameSessionService.this,
+ gameSessionController,
createGameSessionRequest,
+ gameSessionViewHostConfiguration,
gameSessionFuture));
}
};
+ private DisplayManager mDisplayManager;
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ mDisplayManager = this.getSystemService(DisplayManager.class);
+ }
+
@Override
@Nullable
public final IBinder onBind(@Nullable Intent intent) {
@@ -85,12 +100,39 @@ public abstract class GameSessionService extends Service {
return mInterface.asBinder();
}
- private void doCreate(CreateGameSessionRequest createGameSessionRequest,
- AndroidFuture<IBinder> gameSessionFuture) {
+ private void doCreate(
+ IGameSessionController gameSessionController,
+ CreateGameSessionRequest createGameSessionRequest,
+ GameSessionViewHostConfiguration gameSessionViewHostConfiguration,
+ AndroidFuture<CreateGameSessionResult> createGameSessionResultFuture) {
GameSession gameSession = onNewSession(createGameSessionRequest);
Objects.requireNonNull(gameSession);
- gameSessionFuture.complete(gameSession.mInterface.asBinder());
+ Display display = mDisplayManager.getDisplay(gameSessionViewHostConfiguration.mDisplayId);
+ if (display == null) {
+ createGameSessionResultFuture.completeExceptionally(
+ new IllegalStateException("No display found for id: "
+ + gameSessionViewHostConfiguration.mDisplayId));
+ return;
+ }
+
+ IBinder hostToken = new Binder();
+ SurfaceControlViewHost surfaceControlViewHost =
+ new SurfaceControlViewHost(this, display, hostToken);
+
+ gameSession.attach(
+ gameSessionController,
+ createGameSessionRequest.getTaskId(),
+ this,
+ surfaceControlViewHost,
+ gameSessionViewHostConfiguration.mWidthPx,
+ gameSessionViewHostConfiguration.mHeightPx);
+
+ CreateGameSessionResult createGameSessionResult =
+ new CreateGameSessionResult(gameSession.mInterface,
+ surfaceControlViewHost.getSurfacePackage());
+
+ createGameSessionResultFuture.complete(createGameSessionResult);
gameSession.doCreate();
}
diff --git a/core/java/android/service/games/GameSessionViewHostConfiguration.aidl b/core/java/android/service/games/GameSessionViewHostConfiguration.aidl
new file mode 100644
index 000000000000..b900b9d09b07
--- /dev/null
+++ b/core/java/android/service/games/GameSessionViewHostConfiguration.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.games;
+
+/**
+ * @hide
+ */
+parcelable GameSessionViewHostConfiguration;
diff --git a/core/java/android/service/games/GameSessionViewHostConfiguration.java b/core/java/android/service/games/GameSessionViewHostConfiguration.java
new file mode 100644
index 000000000000..53db0dfae8b2
--- /dev/null
+++ b/core/java/android/service/games/GameSessionViewHostConfiguration.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.games;
+
+import android.annotation.Hide;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Represents the configuration of the {@link android.view.SurfaceControlViewHost} used to render
+ * the overlay for a game session.
+ *
+ * @hide
+ */
+@Hide
+public final class GameSessionViewHostConfiguration implements Parcelable {
+
+ @NonNull
+ public static final Creator<GameSessionViewHostConfiguration> CREATOR =
+ new Creator<GameSessionViewHostConfiguration>() {
+ @Override
+ public GameSessionViewHostConfiguration createFromParcel(Parcel source) {
+ return new GameSessionViewHostConfiguration(
+ source.readInt(),
+ source.readInt(),
+ source.readInt());
+ }
+
+ @Override
+ public GameSessionViewHostConfiguration[] newArray(int size) {
+ return new GameSessionViewHostConfiguration[0];
+ }
+ };
+
+ final int mDisplayId;
+ final int mWidthPx;
+ final int mHeightPx;
+
+ public GameSessionViewHostConfiguration(int displayId, int widthPx, int heightPx) {
+ this.mDisplayId = displayId;
+ this.mWidthPx = widthPx;
+ this.mHeightPx = heightPx;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mDisplayId);
+ dest.writeInt(mWidthPx);
+ dest.writeInt(mHeightPx);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof GameSessionViewHostConfiguration)) return false;
+ GameSessionViewHostConfiguration that = (GameSessionViewHostConfiguration) o;
+ return mDisplayId == that.mDisplayId && mWidthPx == that.mWidthPx
+ && mHeightPx == that.mHeightPx;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mDisplayId, mWidthPx, mHeightPx);
+ }
+
+ @Override
+ public String toString() {
+ return "GameSessionViewHostConfiguration{"
+ + "mDisplayId=" + mDisplayId
+ + ", mWidthPx=" + mWidthPx
+ + ", mHeightPx=" + mHeightPx
+ + '}';
+ }
+}
diff --git a/core/java/android/service/games/IGameSession.aidl b/core/java/android/service/games/IGameSession.aidl
index b2e9f1d21f6e..71da6302b63d 100644
--- a/core/java/android/service/games/IGameSession.aidl
+++ b/core/java/android/service/games/IGameSession.aidl
@@ -20,5 +20,6 @@ package android.service.games;
* @hide
*/
oneway interface IGameSession {
- void destroy();
+ void onDestroyed();
+ void onTaskFocusChanged(boolean focused);
}
diff --git a/core/java/android/service/games/IGameSessionController.aidl b/core/java/android/service/games/IGameSessionController.aidl
new file mode 100644
index 000000000000..fe1d3629918e
--- /dev/null
+++ b/core/java/android/service/games/IGameSessionController.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.games;
+
+import com.android.internal.infra.AndroidFuture;
+
+/**
+ * @hide
+ */
+oneway interface IGameSessionController {
+ void takeScreenshot(int taskId, in AndroidFuture gameScreenshotResultFuture);
+} \ No newline at end of file
diff --git a/core/java/android/service/games/IGameSessionService.aidl b/core/java/android/service/games/IGameSessionService.aidl
index 2a53ea7f8e4a..37cde561f549 100644
--- a/core/java/android/service/games/IGameSessionService.aidl
+++ b/core/java/android/service/games/IGameSessionService.aidl
@@ -16,8 +16,10 @@
package android.service.games;
+import android.service.games.IGameSessionController;
import android.service.games.IGameSession;
import android.service.games.CreateGameSessionRequest;
+import android.service.games.GameSessionViewHostConfiguration;
import com.android.internal.infra.AndroidFuture;
@@ -27,6 +29,8 @@ import com.android.internal.infra.AndroidFuture;
*/
oneway interface IGameSessionService {
void create(
+ in IGameSessionController gameSessionController,
in CreateGameSessionRequest createGameSessionRequest,
- in AndroidFuture /* T=IBinder for IGameSession */ gameSessionFuture);
+ in GameSessionViewHostConfiguration gameSessionViewHostConfiguration,
+ in AndroidFuture /* T=CreateGameSessionResult */ createGameSessionResultFuture);
}
diff --git a/core/java/android/service/notification/Condition.java b/core/java/android/service/notification/Condition.java
index 4f324f9e35bf..267b2ff818a6 100644
--- a/core/java/android/service/notification/Condition.java
+++ b/core/java/android/service/notification/Condition.java
@@ -114,7 +114,7 @@ public final class Condition implements Parcelable {
}
public Condition(Parcel source) {
- this((Uri)source.readParcelable(Condition.class.getClassLoader()),
+ this((Uri)source.readParcelable(Condition.class.getClassLoader(), android.net.Uri.class),
source.readString(),
source.readString(),
source.readString(),
diff --git a/core/java/android/service/notification/ConversationChannelWrapper.java b/core/java/android/service/notification/ConversationChannelWrapper.java
index 3d0984ca80ee..35b6bad4e40b 100644
--- a/core/java/android/service/notification/ConversationChannelWrapper.java
+++ b/core/java/android/service/notification/ConversationChannelWrapper.java
@@ -40,10 +40,10 @@ public final class ConversationChannelWrapper implements Parcelable {
public ConversationChannelWrapper() {}
protected ConversationChannelWrapper(Parcel in) {
- mNotificationChannel = in.readParcelable(NotificationChannel.class.getClassLoader());
+ mNotificationChannel = in.readParcelable(NotificationChannel.class.getClassLoader(), android.app.NotificationChannel.class);
mGroupLabel = in.readCharSequence();
mParentChannelLabel = in.readCharSequence();
- mShortcutInfo = in.readParcelable(ShortcutInfo.class.getClassLoader());
+ mShortcutInfo = in.readParcelable(ShortcutInfo.class.getClassLoader(), android.content.pm.ShortcutInfo.class);
mPkg = in.readStringNoHelper();
mUid = in.readInt();
}
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index c94595468aec..ae39d3d3c2da 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -1763,7 +1763,7 @@ public abstract class NotificationListenerService extends Service {
mImportanceExplanation = in.readCharSequence(); // may be null
mRankingScore = in.readFloat();
mOverrideGroupKey = in.readString(); // may be null
- mChannel = in.readParcelable(cl); // may be null
+ mChannel = in.readParcelable(cl, android.app.NotificationChannel.class); // may be null
mOverridePeople = in.createStringArrayList();
mSnoozeCriteria = in.createTypedArrayList(SnoozeCriterion.CREATOR);
mShowBadge = in.readBoolean();
@@ -1776,7 +1776,7 @@ public abstract class NotificationListenerService extends Service {
mCanBubble = in.readBoolean();
mIsTextChanged = in.readBoolean();
mIsConversation = in.readBoolean();
- mShortcutInfo = in.readParcelable(cl);
+ mShortcutInfo = in.readParcelable(cl, android.content.pm.ShortcutInfo.class);
mRankingAdjustment = in.readInt();
mIsBubble = in.readBoolean();
}
diff --git a/core/java/android/service/notification/NotificationRankingUpdate.java b/core/java/android/service/notification/NotificationRankingUpdate.java
index c64f4c46a769..a853714c0e9d 100644
--- a/core/java/android/service/notification/NotificationRankingUpdate.java
+++ b/core/java/android/service/notification/NotificationRankingUpdate.java
@@ -30,7 +30,7 @@ public class NotificationRankingUpdate implements Parcelable {
}
public NotificationRankingUpdate(Parcel in) {
- mRankingMap = in.readParcelable(getClass().getClassLoader());
+ mRankingMap = in.readParcelable(getClass().getClassLoader(), android.service.notification.NotificationListenerService.RankingMap.class);
}
public NotificationListenerService.RankingMap getRankingMap() {
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index c1d5a28aa349..8834ceea7453 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -211,7 +211,7 @@ public class ZenModeConfig implements Parcelable {
allowCallsFrom = source.readInt();
allowMessagesFrom = source.readInt();
user = source.readInt();
- manualRule = source.readParcelable(null);
+ manualRule = source.readParcelable(null, android.service.notification.ZenModeConfig.ZenRule.class);
final int len = source.readInt();
if (len > 0) {
final String[] ids = new String[len];
@@ -1800,10 +1800,10 @@ public class ZenModeConfig implements Parcelable {
name = source.readString();
}
zenMode = source.readInt();
- conditionId = source.readParcelable(null);
- condition = source.readParcelable(null);
- component = source.readParcelable(null);
- configurationActivity = source.readParcelable(null);
+ conditionId = source.readParcelable(null, android.net.Uri.class);
+ condition = source.readParcelable(null, android.service.notification.Condition.class);
+ component = source.readParcelable(null, android.content.ComponentName.class);
+ configurationActivity = source.readParcelable(null, android.content.ComponentName.class);
if (source.readInt() == 1) {
id = source.readString();
}
@@ -1811,7 +1811,7 @@ public class ZenModeConfig implements Parcelable {
if (source.readInt() == 1) {
enabler = source.readString();
}
- zenPolicy = source.readParcelable(null);
+ zenPolicy = source.readParcelable(null, android.service.notification.ZenPolicy.class);
modified = source.readInt() == 1;
pkg = source.readString();
}
diff --git a/core/java/android/service/notification/ZenPolicy.java b/core/java/android/service/notification/ZenPolicy.java
index ed3a9ac33738..a04f07380ce8 100644
--- a/core/java/android/service/notification/ZenPolicy.java
+++ b/core/java/android/service/notification/ZenPolicy.java
@@ -804,8 +804,8 @@ public final class ZenPolicy implements Parcelable {
@Override
public ZenPolicy createFromParcel(Parcel source) {
ZenPolicy policy = new ZenPolicy();
- policy.mPriorityCategories = source.readArrayList(Integer.class.getClassLoader());
- policy.mVisualEffects = source.readArrayList(Integer.class.getClassLoader());
+ policy.mPriorityCategories = source.readArrayList(Integer.class.getClassLoader(), java.lang.Integer.class);
+ policy.mVisualEffects = source.readArrayList(Integer.class.getClassLoader(), java.lang.Integer.class);
policy.mPriorityCalls = source.readInt();
policy.mPriorityMessages = source.readInt();
policy.mConversationSenders = source.readInt();
diff --git a/core/java/android/service/persistentdata/PersistentDataBlockManager.java b/core/java/android/service/persistentdata/PersistentDataBlockManager.java
index 8242f4e2c9dc..44a886257d5a 100644
--- a/core/java/android/service/persistentdata/PersistentDataBlockManager.java
+++ b/core/java/android/service/persistentdata/PersistentDataBlockManager.java
@@ -17,6 +17,7 @@
package android.service.persistentdata;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
@@ -25,6 +26,8 @@ import android.content.Context;
import android.os.RemoteException;
import android.service.oemlock.OemLockManager;
+import com.android.internal.R;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -50,6 +53,7 @@ import java.lang.annotation.RetentionPolicy;
@SystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE)
public class PersistentDataBlockManager {
private static final String TAG = PersistentDataBlockManager.class.getSimpleName();
+ private final Context mContext;
private IPersistentDataBlockService sService;
/**
@@ -74,7 +78,10 @@ public class PersistentDataBlockManager {
public @interface FlashLockState {}
/** @hide */
- public PersistentDataBlockManager(IPersistentDataBlockService service) {
+ public PersistentDataBlockManager(
+ Context context,
+ IPersistentDataBlockService service) {
+ mContext = context;
sService = service;
}
@@ -204,4 +211,15 @@ public class PersistentDataBlockManager {
throw e.rethrowFromSystemServer();
}
}
+
+ /**
+ * Returns the package name which can access the persistent data partition.
+ *
+ * @hide
+ */
+ @SystemApi
+ @NonNull
+ public String getPersistentDataPackageName() {
+ return mContext.getString(R.string.config_persistentDataPackageName);
+ }
}
diff --git a/core/java/android/service/quickaccesswallet/GetWalletCardsResponse.java b/core/java/android/service/quickaccesswallet/GetWalletCardsResponse.java
index 0551e2709de6..7471a4f399a5 100644
--- a/core/java/android/service/quickaccesswallet/GetWalletCardsResponse.java
+++ b/core/java/android/service/quickaccesswallet/GetWalletCardsResponse.java
@@ -63,7 +63,7 @@ public final class GetWalletCardsResponse implements Parcelable {
private static GetWalletCardsResponse readFromParcel(Parcel source) {
int size = source.readInt();
List<WalletCard> walletCards =
- source.readParcelableList(new ArrayList<>(size), WalletCard.class.getClassLoader());
+ source.readParcelableList(new ArrayList<>(size), WalletCard.class.getClassLoader(), android.service.quickaccesswallet.WalletCard.class);
int selectedIndex = source.readInt();
return new GetWalletCardsResponse(walletCards, selectedIndex);
}
diff --git a/core/java/android/service/security/attestationverification/OWNERS b/core/java/android/service/security/attestationverification/OWNERS
new file mode 100644
index 000000000000..12c997868f3c
--- /dev/null
+++ b/core/java/android/service/security/attestationverification/OWNERS
@@ -0,0 +1 @@
+include platform/frameworks/base:/core/java/android/security/attestationverification/OWNERS
diff --git a/core/java/android/service/settings/suggestions/Suggestion.java b/core/java/android/service/settings/suggestions/Suggestion.java
index 3e63efbda9c0..16622d70065f 100644
--- a/core/java/android/service/settings/suggestions/Suggestion.java
+++ b/core/java/android/service/settings/suggestions/Suggestion.java
@@ -120,9 +120,9 @@ public final class Suggestion implements Parcelable {
mId = in.readString();
mTitle = in.readCharSequence();
mSummary = in.readCharSequence();
- mIcon = in.readParcelable(Icon.class.getClassLoader());
+ mIcon = in.readParcelable(Icon.class.getClassLoader(), android.graphics.drawable.Icon.class);
mFlags = in.readInt();
- mPendingIntent = in.readParcelable(PendingIntent.class.getClassLoader());
+ mPendingIntent = in.readParcelable(PendingIntent.class.getClassLoader(), android.app.PendingIntent.class);
}
public static final @android.annotation.NonNull Creator<Suggestion> CREATOR = new Creator<Suggestion>() {
diff --git a/core/java/android/service/smartspace/SmartspaceService.java b/core/java/android/service/smartspace/SmartspaceService.java
index 7dd85cc8f988..b903fbeb035c 100644
--- a/core/java/android/service/smartspace/SmartspaceService.java
+++ b/core/java/android/service/smartspace/SmartspaceService.java
@@ -245,7 +245,9 @@ public abstract class SmartspaceService extends Service {
public abstract void onDestroySmartspaceSession(@NonNull SmartspaceSessionId sessionId);
private void doDestroy(@NonNull SmartspaceSessionId sessionId) {
- Log.d(TAG, "doDestroy mSessionCallbacks: " + mSessionCallbacks);
+ if (DEBUG) {
+ Log.d(TAG, "doDestroy mSessionCallbacks: " + mSessionCallbacks);
+ }
super.onDestroy();
mSessionCallbacks.remove(sessionId);
onDestroySmartspaceSession(sessionId);
diff --git a/core/java/android/service/timezone/TimeZoneProviderEvent.java b/core/java/android/service/timezone/TimeZoneProviderEvent.java
index 700528116a8f..f6433b7f371e 100644
--- a/core/java/android/service/timezone/TimeZoneProviderEvent.java
+++ b/core/java/android/service/timezone/TimeZoneProviderEvent.java
@@ -141,7 +141,7 @@ public final class TimeZoneProviderEvent implements Parcelable {
int type = in.readInt();
long creationElapsedMillis = in.readLong();
TimeZoneProviderSuggestion suggestion =
- in.readParcelable(getClass().getClassLoader());
+ in.readParcelable(getClass().getClassLoader(), android.service.timezone.TimeZoneProviderSuggestion.class);
String failureCause = in.readString8();
return new TimeZoneProviderEvent(
type, creationElapsedMillis, suggestion, failureCause);
diff --git a/core/java/android/service/timezone/TimeZoneProviderSuggestion.java b/core/java/android/service/timezone/TimeZoneProviderSuggestion.java
index 229fa268a47c..4841ac189034 100644
--- a/core/java/android/service/timezone/TimeZoneProviderSuggestion.java
+++ b/core/java/android/service/timezone/TimeZoneProviderSuggestion.java
@@ -100,7 +100,7 @@ public final class TimeZoneProviderSuggestion implements Parcelable {
public TimeZoneProviderSuggestion createFromParcel(Parcel in) {
@SuppressWarnings("unchecked")
ArrayList<String> timeZoneIds =
- (ArrayList<String>) in.readArrayList(null /* classLoader */);
+ (ArrayList<String>) in.readArrayList(null /* classLoader */, java.lang.String.class);
long elapsedRealtimeMillis = in.readLong();
return new TimeZoneProviderSuggestion(timeZoneIds, elapsedRealtimeMillis);
}
diff --git a/core/java/android/service/trust/ITrustAgentService.aidl b/core/java/android/service/trust/ITrustAgentService.aidl
index 21661db0606a..ec3b8575ed36 100644
--- a/core/java/android/service/trust/ITrustAgentService.aidl
+++ b/core/java/android/service/trust/ITrustAgentService.aidl
@@ -25,6 +25,7 @@ import android.service.trust.ITrustAgentServiceCallback;
*/
interface ITrustAgentService {
oneway void onUnlockAttempt(boolean successful);
+ oneway void onUserRequestedUnlock();
oneway void onUnlockLockout(int timeoutMs);
oneway void onTrustTimeout();
oneway void onDeviceLocked();
diff --git a/core/java/android/service/trust/TrustAgentService.java b/core/java/android/service/trust/TrustAgentService.java
index 22ed1b8138b9..fba61cfd801e 100644
--- a/core/java/android/service/trust/TrustAgentService.java
+++ b/core/java/android/service/trust/TrustAgentService.java
@@ -186,6 +186,7 @@ public class TrustAgentService extends Service {
private static final int MSG_ESCROW_TOKEN_ADDED = 7;
private static final int MSG_ESCROW_TOKEN_STATE_RECEIVED = 8;
private static final int MSG_ESCROW_TOKEN_REMOVED = 9;
+ private static final int MSG_USER_REQUESTED_UNLOCK = 10;
private static final String EXTRA_TOKEN = "token";
private static final String EXTRA_TOKEN_HANDLE = "token_handle";
@@ -219,6 +220,9 @@ public class TrustAgentService extends Service {
case MSG_UNLOCK_ATTEMPT:
onUnlockAttempt(msg.arg1 != 0);
break;
+ case MSG_USER_REQUESTED_UNLOCK:
+ onUserRequestedUnlock();
+ break;
case MSG_UNLOCK_LOCKOUT:
onDeviceUnlockLockout(msg.arg1);
break;
@@ -306,7 +310,7 @@ public class TrustAgentService extends Service {
*
* @see #FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE
*
- * TODO(b/213631672): Hook up call from system server & SystemUI, then un-hide
+ * TODO(b/213631672): Add CTS tests
* @hide
*/
public void onUserRequestedUnlock() {
@@ -665,6 +669,11 @@ public class TrustAgentService extends Service {
}
@Override
+ public void onUserRequestedUnlock() {
+ mHandler.obtainMessage(MSG_USER_REQUESTED_UNLOCK).sendToTarget();
+ }
+
+ @Override
public void onUnlockLockout(int timeoutMs) {
mHandler.obtainMessage(MSG_UNLOCK_LOCKOUT, timeoutMs, 0).sendToTarget();
}
diff --git a/core/java/android/service/wallpaper/IWallpaperEngine.aidl b/core/java/android/service/wallpaper/IWallpaperEngine.aidl
index 81af6a220444..93d4def2180e 100644
--- a/core/java/android/service/wallpaper/IWallpaperEngine.aidl
+++ b/core/java/android/service/wallpaper/IWallpaperEngine.aidl
@@ -46,4 +46,5 @@ interface IWallpaperEngine {
oneway void removeLocalColorsAreas(in List<RectF> regions);
oneway void addLocalColorsAreas(in List<RectF> regions);
SurfaceControl mirrorSurfaceControl();
+ oneway void applyDimming(float dimAmount);
}
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 73ffd66486d2..dd4355d3b7a1 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -26,6 +26,7 @@ import static android.view.SurfaceControl.METADATA_WINDOW_TYPE;
import static android.view.View.SYSTEM_UI_FLAG_VISIBLE;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+import android.animation.ValueAnimator;
import android.annotation.FloatRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -159,6 +160,7 @@ public abstract class WallpaperService extends Service {
private static final int MSG_ZOOM = 10100;
private static final int MSG_SCALE_PREVIEW = 10110;
private static final int MSG_REPORT_SHOWN = 10150;
+ private static final int MSG_UPDATE_DIMMING = 10200;
private static final List<Float> PROHIBITED_STEPS = Arrays.asList(0f, Float.POSITIVE_INFINITY,
Float.NEGATIVE_INFINITY);
@@ -167,6 +169,8 @@ public abstract class WallpaperService extends Service {
private static final boolean ENABLE_WALLPAPER_DIMMING =
SystemProperties.getBoolean("persist.debug.enable_wallpaper_dimming", true);
+ private static final long DIMMING_ANIMATION_DURATION_MS = 300L;
+
private final ArrayList<Engine> mActiveEngines
= new ArrayList<Engine>();
@@ -221,6 +225,9 @@ public abstract class WallpaperService extends Service {
boolean mOffsetsChanged;
boolean mFixedSizeAllowed;
boolean mShouldDim;
+ // Whether the wallpaper should be dimmed by default (when no additional dimming is applied)
+ // based on its color hints
+ boolean mShouldDimByDefault;
int mWidth;
int mHeight;
int mFormat;
@@ -271,7 +278,10 @@ public abstract class WallpaperService extends Service {
private Display mDisplay;
private Context mDisplayContext;
private int mDisplayState;
+ private @Surface.Rotation int mDisplayInstallOrientation;
private float mWallpaperDimAmount = 0.05f;
+ private float mPreviousWallpaperDimAmount = mWallpaperDimAmount;
+ private float mDefaultDimAmount = mWallpaperDimAmount;
SurfaceControl mSurfaceControl = new SurfaceControl();
SurfaceControl mBbqSurfaceControl;
@@ -861,15 +871,34 @@ public abstract class WallpaperService extends Service {
return;
}
int colorHints = colors.getColorHints();
- boolean shouldDim = ((colorHints & WallpaperColors.HINT_SUPPORTS_DARK_TEXT) == 0
+ mShouldDimByDefault = ((colorHints & WallpaperColors.HINT_SUPPORTS_DARK_TEXT) == 0
&& (colorHints & WallpaperColors.HINT_SUPPORTS_DARK_THEME) == 0);
- if (shouldDim != mShouldDim) {
- mShouldDim = shouldDim;
+
+ // If default dimming value changes and no additional dimming is applied
+ if (mShouldDimByDefault != mShouldDim && mWallpaperDimAmount == 0f) {
+ mShouldDim = mShouldDimByDefault;
updateSurfaceDimming();
updateSurface(false, false, true);
}
}
+ /**
+ * Update the dim amount of the wallpaper by updating the surface.
+ *
+ * @param dimAmount Float amount between [0.0, 1.0] to dim the wallpaper.
+ */
+ private void updateWallpaperDimming(float dimAmount) {
+ mPreviousWallpaperDimAmount = mWallpaperDimAmount;
+
+ // Custom dim amount cannot be less than the default dim amount.
+ mWallpaperDimAmount = Math.max(mDefaultDimAmount, dimAmount);
+ // If dim amount is 0f (additional dimming is removed), then the wallpaper should dim
+ // based on its default wallpaper color hints.
+ mShouldDim = dimAmount != 0f || mShouldDimByDefault;
+ updateSurfaceDimming();
+ updateSurface(false, false, true);
+ }
+
private void updateSurfaceDimming() {
if (!ENABLE_WALLPAPER_DIMMING || mBbqSurfaceControl == null) {
return;
@@ -878,9 +907,21 @@ public abstract class WallpaperService extends Service {
// preview mode.
if (!isPreview() && mShouldDim) {
Log.v(TAG, "Setting wallpaper dimming: " + mWallpaperDimAmount);
- new SurfaceControl.Transaction()
- .setAlpha(mBbqSurfaceControl, 1 - mWallpaperDimAmount)
- .apply();
+ SurfaceControl.Transaction surfaceControl = new SurfaceControl.Transaction();
+
+ // Animate dimming to gradually change the wallpaper alpha from the previous
+ // dim amount to the new amount only if the dim amount changed.
+ ValueAnimator animator = ValueAnimator.ofFloat(
+ mPreviousWallpaperDimAmount, mWallpaperDimAmount);
+ animator.setDuration(mPreviousWallpaperDimAmount == mWallpaperDimAmount
+ ? 0 : DIMMING_ANIMATION_DURATION_MS);
+ animator.addUpdateListener((ValueAnimator va) -> {
+ final float dimValue = (float) va.getAnimatedValue();
+ surfaceControl
+ .setAlpha(mBbqSurfaceControl, 1 - dimValue)
+ .apply();
+ });
+ animator.start();
} else {
Log.v(TAG, "Setting wallpaper dimming: " + 0);
new SurfaceControl.Transaction()
@@ -1082,6 +1123,11 @@ public abstract class WallpaperService extends Service {
mWindow, mLayout, mWidth, mHeight,
View.VISIBLE, 0, -1, mWinFrames, mMergedConfiguration, mSurfaceControl,
mInsetsState, mTempControls, mSurfaceSize);
+
+ final int transformHint = SurfaceControl.rotationToBufferTransform(
+ (mDisplayInstallOrientation + mDisplay.getRotation()) % 4);
+ mSurfaceControl.setTransformHint(transformHint);
+
if (mSurfaceControl.isValid()) {
if (mBbqSurfaceControl == null) {
mBbqSurfaceControl = new SurfaceControl.Builder()
@@ -1095,9 +1141,9 @@ public abstract class WallpaperService extends Service {
.build();
updateSurfaceDimming();
}
- // Propagate transform hint from WM so we can use the right hint for the
+ // Propagate transform hint from WM, so we can use the right hint for the
// first frame.
- mBbqSurfaceControl.setTransformHint(mSurfaceControl.getTransformHint());
+ mBbqSurfaceControl.setTransformHint(transformHint);
Surface blastSurface = getOrCreateBLASTSurface(mSurfaceSize.x,
mSurfaceSize.y, mFormat);
// If blastSurface == null that means it hasn't changed since the last
@@ -1332,9 +1378,12 @@ public abstract class WallpaperService extends Service {
// Use window context of TYPE_WALLPAPER so client can access UI resources correctly.
mDisplayContext = createDisplayContext(mDisplay)
.createWindowContext(TYPE_WALLPAPER, null /* options */);
- mWallpaperDimAmount = mDisplayContext.getResources().getFloat(
+ mDefaultDimAmount = mDisplayContext.getResources().getFloat(
com.android.internal.R.dimen.config_wallpaperDimAmount);
+ mWallpaperDimAmount = mDefaultDimAmount;
+ mPreviousWallpaperDimAmount = mWallpaperDimAmount;
mDisplayState = mDisplay.getState();
+ mDisplayInstallOrientation = mDisplay.getInstallOrientation();
if (DEBUG) Log.v(TAG, "onCreate(): " + this);
onCreate(mSurfaceHolder);
@@ -1587,6 +1636,7 @@ public abstract class WallpaperService extends Service {
return;
}
Surface surface = mSurfaceHolder.getSurface();
+ if (!surface.isValid()) return;
boolean widthIsLarger = mSurfaceSize.x > mSurfaceSize.y;
int smaller = widthIsLarger ? mSurfaceSize.x
: mSurfaceSize.y;
@@ -1647,7 +1697,7 @@ public abstract class WallpaperService extends Service {
Log.e(TAG, "Error creating page local color bitmap", e);
continue;
}
- WallpaperColors color = WallpaperColors.fromBitmap(target);
+ WallpaperColors color = WallpaperColors.fromBitmap(target, mWallpaperDimAmount);
target.recycle();
WallpaperColors currentColor = page.getColors(area);
@@ -2175,6 +2225,12 @@ public abstract class WallpaperService extends Service {
mDetached.set(true);
}
+ public void applyDimming(float dimAmount) throws RemoteException {
+ Message msg = mCaller.obtainMessageI(MSG_UPDATE_DIMMING,
+ Float.floatToIntBits(dimAmount));
+ mCaller.sendMessage(msg);
+ }
+
public void scalePreview(Rect position) {
Message msg = mCaller.obtainMessageO(MSG_SCALE_PREVIEW, position);
mCaller.sendMessage(msg);
@@ -2245,6 +2301,9 @@ public abstract class WallpaperService extends Service {
case MSG_ZOOM:
mEngine.setZoom(Float.intBitsToFloat(message.arg1));
break;
+ case MSG_UPDATE_DIMMING:
+ mEngine.updateWallpaperDimming(Float.intBitsToFloat(message.arg1));
+ break;
case MSG_SCALE_PREVIEW:
mEngine.scalePreview((Rect) message.obj);
break;
diff --git a/core/java/android/service/wallpapereffectsgeneration/OWNERS b/core/java/android/service/wallpapereffectsgeneration/OWNERS
new file mode 100644
index 000000000000..d2d3e2c0a7b6
--- /dev/null
+++ b/core/java/android/service/wallpapereffectsgeneration/OWNERS
@@ -0,0 +1,4 @@
+susharon@google.com
+shanh@google.com
+huiwu@google.com
+srazdan@google.com
diff --git a/core/java/android/speech/tts/Voice.java b/core/java/android/speech/tts/Voice.java
index 7ffe5eb7893d..0d98a6ca5f14 100644
--- a/core/java/android/speech/tts/Voice.java
+++ b/core/java/android/speech/tts/Voice.java
@@ -84,7 +84,7 @@ public class Voice implements Parcelable {
private Voice(Parcel in) {
this.mName = in.readString();
- this.mLocale = (Locale)in.readSerializable();
+ this.mLocale = (Locale)in.readSerializable(java.util.Locale.class.getClassLoader(), java.util.Locale.class);
this.mQuality = in.readInt();
this.mLatency = in.readInt();
this.mRequiresNetworkConnection = (in.readByte() == 1);
diff --git a/core/java/android/telephony/SubscriptionPlan.java b/core/java/android/telephony/SubscriptionPlan.java
index d5ac4368aa97..fb2d7714d402 100644
--- a/core/java/android/telephony/SubscriptionPlan.java
+++ b/core/java/android/telephony/SubscriptionPlan.java
@@ -99,7 +99,7 @@ public final class SubscriptionPlan implements Parcelable {
}
private SubscriptionPlan(Parcel source) {
- cycleRule = source.readParcelable(null);
+ cycleRule = source.readParcelable(null, android.util.RecurrenceRule.class);
title = source.readCharSequence();
summary = source.readCharSequence();
dataLimitBytes = source.readLong();
diff --git a/core/java/android/text/FontConfig.java b/core/java/android/text/FontConfig.java
index 2f7fb2f0ab9d..32b3bc62a8cd 100644
--- a/core/java/android/text/FontConfig.java
+++ b/core/java/android/text/FontConfig.java
@@ -143,9 +143,9 @@ public final class FontConfig implements Parcelable {
@Override
public FontConfig createFromParcel(Parcel source) {
List<FontFamily> families = source.readParcelableList(new ArrayList<>(),
- FontFamily.class.getClassLoader());
+ FontFamily.class.getClassLoader(), android.text.FontConfig.FontFamily.class);
List<Alias> aliases = source.readParcelableList(new ArrayList<>(),
- Alias.class.getClassLoader());
+ Alias.class.getClassLoader(), android.text.FontConfig.Alias.class);
long lastModifiedDate = source.readLong();
int configVersion = source.readInt();
return new FontConfig(families, aliases, lastModifiedDate, configVersion);
@@ -617,7 +617,7 @@ public final class FontConfig implements Parcelable {
@Override
public FontFamily createFromParcel(Parcel source) {
List<Font> fonts = source.readParcelableList(
- new ArrayList<>(), Font.class.getClassLoader());
+ new ArrayList<>(), Font.class.getClassLoader(), android.text.FontConfig.Font.class);
String name = source.readString8();
String langTags = source.readString8();
int variant = source.readInt();
diff --git a/core/java/android/text/PrecomputedText.java b/core/java/android/text/PrecomputedText.java
index ce63376a6d63..be66db2a4c05 100644
--- a/core/java/android/text/PrecomputedText.java
+++ b/core/java/android/text/PrecomputedText.java
@@ -363,6 +363,9 @@ public class PrecomputedText implements Spannable {
public String toString() {
int lineBreakStyle = (mLineBreakConfig != null)
? mLineBreakConfig.getLineBreakStyle() : LineBreakConfig.LINE_BREAK_STYLE_NONE;
+ int lineBreakWordStyle = (mLineBreakConfig != null)
+ ? mLineBreakConfig.getLineBreakWordStyle()
+ : LineBreakConfig.LINE_BREAK_WORD_STYLE_NONE;
return "{"
+ "textSize=" + mPaint.getTextSize()
+ ", textScaleX=" + mPaint.getTextScaleX()
@@ -376,6 +379,7 @@ public class PrecomputedText implements Spannable {
+ ", breakStrategy=" + mBreakStrategy
+ ", hyphenationFrequency=" + mHyphenationFrequency
+ ", lineBreakStyle=" + lineBreakStyle
+ + ", lineBreakWordStyle=" + lineBreakWordStyle
+ "}";
}
};
diff --git a/core/java/android/text/style/EasyEditSpan.java b/core/java/android/text/style/EasyEditSpan.java
index ccccdcf88b69..3da83332d62e 100644
--- a/core/java/android/text/style/EasyEditSpan.java
+++ b/core/java/android/text/style/EasyEditSpan.java
@@ -82,7 +82,7 @@ public class EasyEditSpan implements ParcelableSpan {
* Constructor called from {@link TextUtils} to restore the span.
*/
public EasyEditSpan(@NonNull Parcel source) {
- mPendingIntent = source.readParcelable(null);
+ mPendingIntent = source.readParcelable(null, android.app.PendingIntent.class);
mDeleteEnabled = (source.readByte() == 1);
}
diff --git a/core/java/android/text/style/TextAppearanceSpan.java b/core/java/android/text/style/TextAppearanceSpan.java
index 23557694a48d..adb379a397b7 100644
--- a/core/java/android/text/style/TextAppearanceSpan.java
+++ b/core/java/android/text/style/TextAppearanceSpan.java
@@ -249,7 +249,7 @@ public class TextAppearanceSpan extends MetricAffectingSpan implements Parcelabl
mTypeface = LeakyTypefaceStorage.readTypefaceFromParcel(src);
mTextFontWeight = src.readInt();
- mTextLocales = src.readParcelable(LocaleList.class.getClassLoader());
+ mTextLocales = src.readParcelable(LocaleList.class.getClassLoader(), android.os.LocaleList.class);
mShadowRadius = src.readFloat();
mShadowDx = src.readFloat();
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 52f1faef0fc3..96a1fc60e458 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -81,6 +81,7 @@ public class FeatureFlagUtils {
DEFAULT_FLAGS.put(SETTINGS_USE_NEW_BACKUP_ELIGIBILITY_RULES, "true");
DEFAULT_FLAGS.put(SETTINGS_ENABLE_SECURITY_HUB, "true");
DEFAULT_FLAGS.put(SETTINGS_SUPPORT_LARGE_SCREEN, "true");
+ DEFAULT_FLAGS.put("settings_search_always_expand", "false");
DEFAULT_FLAGS.put(SETTINGS_APP_LANGUAGE_SELECTION, "false");
DEFAULT_FLAGS.put(SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS, "true");
}
diff --git a/core/java/android/util/MemoryIntArray.java b/core/java/android/util/MemoryIntArray.java
index 42181c3c1722..5cbbbef2cf88 100644
--- a/core/java/android/util/MemoryIntArray.java
+++ b/core/java/android/util/MemoryIntArray.java
@@ -80,7 +80,7 @@ public final class MemoryIntArray implements Parcelable, Closeable {
private MemoryIntArray(Parcel parcel) throws IOException {
mIsOwner = false;
- ParcelFileDescriptor pfd = parcel.readParcelable(null);
+ ParcelFileDescriptor pfd = parcel.readParcelable(null, android.os.ParcelFileDescriptor.class);
if (pfd == null) {
throw new IOException("No backing file descriptor");
}
diff --git a/core/java/android/util/SparseDoubleArray.java b/core/java/android/util/SparseDoubleArray.java
index dc93a473fe44..e8d96d8e26e4 100644
--- a/core/java/android/util/SparseDoubleArray.java
+++ b/core/java/android/util/SparseDoubleArray.java
@@ -81,9 +81,17 @@ public class SparseDoubleArray implements Cloneable {
* if no such mapping has been made.
*/
public double get(int key) {
+ return get(key, 0);
+ }
+
+ /**
+ * Gets the double mapped from the specified key, or the specified value
+ * if no such mapping has been made.
+ */
+ public double get(int key, double valueIfKeyNotFound) {
final int index = mValues.indexOfKey(key);
if (index < 0) {
- return 0.0d;
+ return valueIfKeyNotFound;
}
return valueAt(index);
}
@@ -105,7 +113,7 @@ public class SparseDoubleArray implements Cloneable {
* <p>This differs from {@link #put} because instead of replacing any previous value, it adds
* (in the numerical sense) to it.
*/
- public void add(int key, double summand) {
+ public void incrementValue(int key, double summand) {
final double oldValue = get(key);
put(key, oldValue + summand);
}
@@ -138,6 +146,13 @@ public class SparseDoubleArray implements Cloneable {
}
/**
+ * Removes all key-value mappings from this SparseDoubleArray.
+ */
+ public void clear() {
+ mValues.clear();
+ }
+
+ /**
* {@inheritDoc}
*
* <p>This implementation composes a string by iterating over its mappings.
diff --git a/core/java/android/util/SparseLongArray.java b/core/java/android/util/SparseLongArray.java
index f2bc0c5a34d6..7185972b85bf 100644
--- a/core/java/android/util/SparseLongArray.java
+++ b/core/java/android/util/SparseLongArray.java
@@ -164,6 +164,30 @@ public class SparseLongArray implements Cloneable {
}
/**
+ * Adds a mapping from the specified key to the specified value,
+ * <b>adding</b> its value to the previous mapping from the specified key if there
+ * was one.
+ *
+ * <p>This differs from {@link #put} because instead of replacing any previous value, it adds
+ * (in the numerical sense) to it.
+ *
+ * @hide
+ */
+ public void incrementValue(int key, long summand) {
+ int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
+
+ if (i >= 0) {
+ mValues[i] += summand;
+ } else {
+ i = ~i;
+
+ mKeys = GrowingArrayUtils.insert(mKeys, mSize, i, key);
+ mValues = GrowingArrayUtils.insert(mValues, mSize, i, summand);
+ mSize++;
+ }
+ }
+
+ /**
* Returns the number of key-value mappings that this SparseLongArray
* currently stores.
*/
diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java
index 9b8523f9b006..b8eb6027b09c 100644
--- a/core/java/android/view/Choreographer.java
+++ b/core/java/android/view/Choreographer.java
@@ -779,7 +779,7 @@ public final class Choreographer {
+ "time to " + (lastFrameOffset * 0.000001f) + " ms in the past.");
}
frameTimeNanos = startNanos - lastFrameOffset;
- frameData.setFrameTimeNanos(-lastFrameOffset);
+ frameData.setFrameTimeNanos(frameTimeNanos);
}
if (frameTimeNanos < mLastFrameTimeNanos) {
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 70266c1717a1..d6e074fbe178 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -928,6 +928,18 @@ public final class Display {
}
/**
+ * Returns the install orientation of the display.
+ * @hide
+ */
+ @Surface.Rotation
+ public int getInstallOrientation() {
+ synchronized (mLock) {
+ updateDisplayInfoLocked();
+ return mDisplayInfo.installOrientation;
+ }
+ }
+
+ /**
* @deprecated use {@link #getRotation}
* @return orientation of this display.
*/
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index b8614ccde6fd..6917d664327f 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -318,6 +318,12 @@ public final class DisplayInfo implements Parcelable {
@Nullable
public RoundedCorners roundedCorners;
+ /**
+ * Install orientation of the display relative to its natural orientation.
+ */
+ @Surface.Rotation
+ public int installOrientation;
+
public static final @android.annotation.NonNull Creator<DisplayInfo> CREATOR = new Creator<DisplayInfo>() {
@Override
public DisplayInfo createFromParcel(Parcel source) {
@@ -389,7 +395,8 @@ public final class DisplayInfo implements Parcelable {
&& brightnessMaximum == other.brightnessMaximum
&& brightnessDefault == other.brightnessDefault
&& Objects.equals(roundedCorners, other.roundedCorners)
- && shouldConstrainMetricsForLauncher == other.shouldConstrainMetricsForLauncher;
+ && shouldConstrainMetricsForLauncher == other.shouldConstrainMetricsForLauncher
+ && installOrientation == other.installOrientation;
}
@Override
@@ -441,6 +448,7 @@ public final class DisplayInfo implements Parcelable {
brightnessDefault = other.brightnessDefault;
roundedCorners = other.roundedCorners;
shouldConstrainMetricsForLauncher = other.shouldConstrainMetricsForLauncher;
+ installOrientation = other.installOrientation;
}
public void readFromParcel(Parcel source) {
@@ -449,8 +457,8 @@ public final class DisplayInfo implements Parcelable {
type = source.readInt();
displayId = source.readInt();
displayGroupId = source.readInt();
- address = source.readParcelable(null);
- deviceProductInfo = source.readParcelable(null);
+ address = source.readParcelable(null, android.view.DisplayAddress.class);
+ deviceProductInfo = source.readParcelable(null, android.hardware.display.DeviceProductInfo.class);
name = source.readString8();
appWidth = source.readInt();
appHeight = source.readInt();
@@ -475,7 +483,7 @@ public final class DisplayInfo implements Parcelable {
for (int i = 0; i < nColorModes; i++) {
supportedColorModes[i] = source.readInt();
}
- hdrCapabilities = source.readParcelable(null);
+ hdrCapabilities = source.readParcelable(null, android.view.Display.HdrCapabilities.class);
minimalPostProcessingSupported = source.readBoolean();
logicalDensityDpi = source.readInt();
physicalXDpi = source.readFloat();
@@ -498,6 +506,7 @@ public final class DisplayInfo implements Parcelable {
userDisabledHdrTypes[i] = source.readInt();
}
shouldConstrainMetricsForLauncher = source.readBoolean();
+ installOrientation = source.readInt();
}
@Override
@@ -553,6 +562,7 @@ public final class DisplayInfo implements Parcelable {
dest.writeInt(userDisabledHdrTypes[i]);
}
dest.writeBoolean(shouldConstrainMetricsForLauncher);
+ dest.writeInt(installOrientation);
}
@Override
@@ -809,6 +819,8 @@ public final class DisplayInfo implements Parcelable {
sb.append(brightnessDefault);
sb.append(", shouldConstrainMetricsForLauncher ");
sb.append(shouldConstrainMetricsForLauncher);
+ sb.append(", installOrientation ");
+ sb.append(Surface.rotationToString(installOrientation));
sb.append("}");
return sb.toString();
}
diff --git a/core/java/android/view/GestureExclusionTracker.java b/core/java/android/view/GestureExclusionTracker.java
deleted file mode 100644
index fffb323e6d50..000000000000
--- a/core/java/android/view/GestureExclusionTracker.java
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.graphics.Rect;
-
-import com.android.internal.util.Preconditions;
-
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
-
-/**
- * Used by {@link ViewRootImpl} to track system gesture exclusion rects reported by views.
- */
-class GestureExclusionTracker {
- private boolean mGestureExclusionViewsChanged = false;
- private boolean mRootGestureExclusionRectsChanged = false;
- private List<Rect> mRootGestureExclusionRects = Collections.emptyList();
- private List<GestureExclusionViewInfo> mGestureExclusionViewInfos = new ArrayList<>();
- private List<Rect> mGestureExclusionRects = Collections.emptyList();
-
- public void updateRectsForView(@NonNull View view) {
- boolean found = false;
- final Iterator<GestureExclusionViewInfo> i = mGestureExclusionViewInfos.iterator();
- while (i.hasNext()) {
- final GestureExclusionViewInfo info = i.next();
- final View v = info.getView();
- if (v == null || !v.isAttachedToWindow() || !v.isAggregatedVisible()) {
- mGestureExclusionViewsChanged = true;
- i.remove();
- continue;
- }
- if (v == view) {
- found = true;
- info.mDirty = true;
- break;
- }
- }
- if (!found && view.isAttachedToWindow()) {
- mGestureExclusionViewInfos.add(new GestureExclusionViewInfo(view));
- mGestureExclusionViewsChanged = true;
- }
- }
-
- @Nullable
- public List<Rect> computeChangedRects() {
- boolean changed = mRootGestureExclusionRectsChanged;
- final Iterator<GestureExclusionViewInfo> i = mGestureExclusionViewInfos.iterator();
- final List<Rect> rects = new ArrayList<>(mRootGestureExclusionRects);
- while (i.hasNext()) {
- final GestureExclusionViewInfo info = i.next();
- switch (info.update()) {
- case GestureExclusionViewInfo.CHANGED:
- changed = true;
- // Deliberate fall-through
- case GestureExclusionViewInfo.UNCHANGED:
- rects.addAll(info.mExclusionRects);
- break;
- case GestureExclusionViewInfo.GONE:
- mGestureExclusionViewsChanged = true;
- i.remove();
- break;
- }
- }
- if (changed || mGestureExclusionViewsChanged) {
- mGestureExclusionViewsChanged = false;
- mRootGestureExclusionRectsChanged = false;
- if (!mGestureExclusionRects.equals(rects)) {
- mGestureExclusionRects = rects;
- return rects;
- }
- }
- return null;
- }
-
- public void setRootSystemGestureExclusionRects(@NonNull List<Rect> rects) {
- Preconditions.checkNotNull(rects, "rects must not be null");
- mRootGestureExclusionRects = rects;
- mRootGestureExclusionRectsChanged = true;
- }
-
- @NonNull
- public List<Rect> getRootSystemGestureExclusionRects() {
- return mRootGestureExclusionRects;
- }
-
- private static class GestureExclusionViewInfo {
- public static final int CHANGED = 0;
- public static final int UNCHANGED = 1;
- public static final int GONE = 2;
-
- private final WeakReference<View> mView;
- boolean mDirty = true;
- List<Rect> mExclusionRects = Collections.emptyList();
-
- GestureExclusionViewInfo(View view) {
- mView = new WeakReference<>(view);
- }
-
- public View getView() {
- return mView.get();
- }
-
- public int update() {
- final View excludedView = getView();
- if (excludedView == null || !excludedView.isAttachedToWindow()
- || !excludedView.isAggregatedVisible()) return GONE;
- final List<Rect> localRects = excludedView.getSystemGestureExclusionRects();
- final List<Rect> newRects = new ArrayList<>(localRects.size());
- for (Rect src : localRects) {
- Rect mappedRect = new Rect(src);
- ViewParent p = excludedView.getParent();
- if (p != null && p.getChildVisibleRect(excludedView, mappedRect, null)) {
- newRects.add(mappedRect);
- }
- }
-
- if (mExclusionRects.equals(localRects)) return UNCHANGED;
- mExclusionRects = newRects;
- return CHANGED;
- }
- }
-}
diff --git a/core/java/android/view/HandwritingInitiator.java b/core/java/android/view/HandwritingInitiator.java
index 8524ac846d1a..097d1d0df51b 100644
--- a/core/java/android/view/HandwritingInitiator.java
+++ b/core/java/android/view/HandwritingInitiator.java
@@ -19,7 +19,6 @@ package android.view;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Rect;
-import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import com.android.internal.annotations.VisibleForTesting;
@@ -75,11 +74,6 @@ public class HandwritingInitiator {
@VisibleForTesting
public WeakReference<View> mConnectedView = null;
- /** The editor bound reported by the connected View. */
- @Nullable
- @VisibleForTesting
- public Rect mEditorBound = null;
-
/**
* When InputConnection restarts for a View, View#onInputConnectionCreatedInternal
* might be called before View#onInputConnectionClosedInternal, so we need to count the input
@@ -174,9 +168,8 @@ public class HandwritingInitiator {
* @param view the view that created the current InputConnection.
* @see #onInputConnectionClosed(View)
*/
- public void onInputConnectionCreated(@NonNull View view, @NonNull EditorInfo editorInfo) {
+ public void onInputConnectionCreated(@NonNull View view) {
final View connectedView = getConnectedView();
-// updateEditorBound(editorInfo.getInitialEditorBound());
if (connectedView == view) {
++mConnectionCount;
} else {
@@ -198,33 +191,15 @@ public class HandwritingInitiator {
--mConnectionCount;
if (mConnectionCount == 0) {
mConnectedView = null;
- mEditorBound = null;
}
} else {
// Unexpected branch, set mConnectedView to null to avoid further problem.
mConnectedView = null;
- mEditorBound = null;
mConnectionCount = 0;
}
}
/**
- * Notify the HandwritingInitiator that editor bound of the connected view(the view with
- * active InputConnection) has be updated.
- * @param editorBound new the editor bounds of the connected view.
- */
- public void updateEditorBound(@NonNull Rect editorBound) {
- if (mEditorBound == null) {
- mEditorBound = new Rect(editorBound);
- } else {
- mEditorBound.left = editorBound.left;
- mEditorBound.top = editorBound.top;
- mEditorBound.right = editorBound.right;
- mEditorBound.bottom = editorBound.bottom;
- }
- }
-
- /**
* Try to initiate handwriting. For this method to successfully send startHandwriting signal,
* the following 3 conditions should meet:
* a) The stylus movement exceeds the touchSlop.
@@ -240,18 +215,19 @@ public class HandwritingInitiator {
return;
}
final View connectedView = getConnectedView();
- if (connectedView == null || mEditorBound == null) {
+ if (connectedView == null) {
return;
}
final ViewParent viewParent = connectedView.getParent();
// Do a final check before startHandwriting.
if (viewParent != null && connectedView.isAttachedToWindow()) {
- final Rect editorBounds = new Rect(mEditorBound);
+ final Rect editorBounds =
+ new Rect(0, 0, connectedView.getWidth(), connectedView.getHeight());
if (viewParent.getChildVisibleRect(connectedView, editorBounds, null)) {
final int roundedInitX = Math.round(mState.mStylusDownX);
final int roundedInitY = Math.round(mState.mStylusDownY);
if (editorBounds.contains(roundedInitX, roundedInitY)) {
- startHandwriting(mConnectedView.get());
+ startHandwriting(connectedView);
}
}
}
@@ -261,7 +237,7 @@ public class HandwritingInitiator {
/** For test only. */
@VisibleForTesting
public void startHandwriting(View view) {
- // mImm.startHandwriting(view);
+ mImm.startStylusHandwriting(view);
}
private boolean largerThanTouchSlop(float x1, float y1, float x2, float y2) {
diff --git a/core/java/android/view/HapticFeedbackConstants.java b/core/java/android/view/HapticFeedbackConstants.java
index c5bc99d042d7..45b65e551305 100644
--- a/core/java/android/view/HapticFeedbackConstants.java
+++ b/core/java/android/view/HapticFeedbackConstants.java
@@ -177,6 +177,10 @@ public class HapticFeedbackConstants {
* Flag for {@link View#performHapticFeedback(int, int)
* View.performHapticFeedback(int, int)}: Ignore the global setting
* for whether to perform haptic feedback, do it always.
+ *
+ * @deprecated Starting from {@link android.os.Build.VERSION_CODES#TIRAMISU} only privileged
+ * apps can ignore user settings for touch feedback.
*/
+ @Deprecated
public static final int FLAG_IGNORE_GLOBAL_SETTING = 0x0002;
}
diff --git a/core/java/android/view/IDisplayWindowListener.aidl b/core/java/android/view/IDisplayWindowListener.aidl
index f95d6b349221..449e9b325904 100644
--- a/core/java/android/view/IDisplayWindowListener.aidl
+++ b/core/java/android/view/IDisplayWindowListener.aidl
@@ -16,8 +16,11 @@
package android.view;
+import android.graphics.Rect;
import android.content.res.Configuration;
+import java.util.List;
+
/**
* Interface to listen for changes to display window-containers.
*
@@ -56,4 +59,9 @@ oneway interface IDisplayWindowListener {
* Called when the previous fixed rotation on a display is finished.
*/
void onFixedRotationFinished(int displayId);
+
+ /**
+ * Called when the keep clear ares on a display have changed.
+ */
+ void onKeepClearAreasChanged(int displayId, in List<Rect> keepClearAreas);
}
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 2c766bd6fd0d..c5ccc18b0cd4 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -25,7 +25,6 @@ import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.GraphicBuffer;
-import android.graphics.Insets;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
@@ -66,6 +65,7 @@ import android.view.WindowManager;
import android.view.SurfaceControl;
import android.view.displayhash.DisplayHash;
import android.view.displayhash.VerifiedDisplayHash;
+import android.window.IOnFpsCallbackListener;
/**
* System private interface to the window manager.
@@ -484,16 +484,6 @@ interface IWindowManager
void getStableInsets(int displayId, out Rect outInsets);
/**
- * Set the forwarded insets on the display.
- * <p>
- * This is only used in case a virtual display is displayed on another display that has insets,
- * and the bounds of the virtual display is overlapping with the insets from the host display.
- * In that case, the contents on the virtual display won't be placed over the forwarded insets.
- * Only the owner of the display is permitted to set the forwarded insets on it.
- */
- void setForwardedInsets(int displayId, in Insets insets);
-
- /**
* Register shortcut key. Shortcut code is packed as:
* (MetaState << Integer.SIZE) | KeyCode
* @hide
@@ -551,6 +541,11 @@ interface IWindowManager
void stopWindowTrace();
/**
+ * If window tracing is active, saves the window trace to file, otherwise does nothing
+ */
+ void saveWindowTraceToFile();
+
+ /**
* Returns true if window trace is enabled.
*/
boolean isWindowTraceEnabled();
@@ -922,4 +917,28 @@ interface IWindowManager
* reverts to using the default task transition with no spec changes.
*/
void clearTaskTransitionSpec();
+
+ /**
+ * Registers the frame rate per second count callback for one given task ID.
+ * Each callback can only register for receiving FPS callback for one task id until unregister
+ * is called. If there's no task associated with the given task id,
+ * {@link IllegalArgumentException} will be thrown. If a task id destroyed after a callback is
+ * registered, the registered callback will not be unregistered until
+ * {@link unregisterTaskFpsCallback()} is called
+ * @param taskId task id of the task.
+ * @param listener listener to be registered.
+ *
+ * @hide
+ */
+ void registerTaskFpsCallback(in int taskId, in IOnFpsCallbackListener listener);
+
+ /**
+ * Unregisters the frame rate per second count callback which was registered with
+ * {@link #registerTaskFpsCallback(int,TaskFpsCallback)}.
+ *
+ * @param listener listener to be unregistered.
+ *
+ * @hide
+ */
+ void unregisterTaskFpsCallback(in IOnFpsCallbackListener listener);
}
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 921ce53b3bda..62265663804f 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -37,6 +37,7 @@ import android.view.Surface;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
import android.window.ClientWindowFrames;
+import android.window.IOnBackInvokedCallback;
import java.util.List;
@@ -290,6 +291,11 @@ interface IWindowSession {
oneway void reportSystemGestureExclusionChanged(IWindow window, in List<Rect> exclusionRects);
/**
+ * Called when the keep-clear areas for this window have changed.
+ */
+ oneway void reportKeepClearAreasChanged(IWindow window, in List<Rect> keepClearRects);
+
+ /**
* Request the server to call setInputWindowInfo on a given Surface, and return
* an input channel where the client can receive input.
*/
@@ -328,4 +334,12 @@ interface IWindowSession {
*/
oneway void generateDisplayHash(IWindow window, in Rect boundsInWindow,
in String hashAlgorithm, in RemoteCallback callback);
+
+ /**
+ * Sets the {@link IOnBackInvokedCallback} to be invoked for a window when back is triggered.
+ *
+ * @param window The token for the window to set the callback to.
+ * @param callback The {@link IOnBackInvokedCallback} to set.
+ */
+ oneway void setOnBackInvokedCallback(IWindow window, IOnBackInvokedCallback callback);
}
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index 4f1354d7eee6..188d7459f9a7 100644
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -572,6 +572,8 @@ public final class InputDevice implements Parcelable {
* @return The identifier object for this device
* @hide
*/
+ @TestApi
+ @NonNull
public InputDeviceIdentifier getIdentifier() {
return mIdentifier;
}
@@ -735,6 +737,21 @@ public final class InputDevice implements Parcelable {
}
/**
+ * Gets the key code produced by the specified location on a US keyboard layout.
+ * Key code as defined in {@link android.view.KeyEvent}.
+ * This API is only functional for devices with {@link InputDevice#SOURCE_KEYBOARD} available
+ * which can alter their key mapping using country specific keyboard layouts.
+ *
+ * @param locationKeyCode The location of a key on a US keyboard layout.
+ * @return The key code produced when pressing the key at the specified location, given the
+ * active keyboard layout. Returns {@link KeyEvent#KEYCODE_UNKNOWN} if the requested
+ * mapping could not be determined, or if an error occurred.
+ */
+ public int getKeyCodeForKeyLocation(int locationKeyCode) {
+ return InputManager.getInstance().getKeyCodeForKeyLocation(mId, locationKeyCode);
+ }
+
+ /**
* Gets information about the range of values for a particular {@link MotionEvent} axis.
* If the device supports multiple sources, the same axis may have different meanings
* for each source. Returns information about the first axis found for any source.
diff --git a/core/java/android/view/KeyboardShortcutInfo.java b/core/java/android/view/KeyboardShortcutInfo.java
index 2660e74dcb20..118b03ce5504 100644
--- a/core/java/android/view/KeyboardShortcutInfo.java
+++ b/core/java/android/view/KeyboardShortcutInfo.java
@@ -91,7 +91,7 @@ public final class KeyboardShortcutInfo implements Parcelable {
private KeyboardShortcutInfo(Parcel source) {
mLabel = source.readCharSequence();
- mIcon = source.readParcelable(null);
+ mIcon = source.readParcelable(null, android.graphics.drawable.Icon.class);
mBaseCharacter = (char) source.readInt();
mKeycode = source.readInt();
mModifiers = source.readInt();
diff --git a/core/java/android/view/OnBackInvokedCallback.java b/core/java/android/view/OnBackInvokedCallback.java
new file mode 100644
index 000000000000..b5cd89cfa4e1
--- /dev/null
+++ b/core/java/android/view/OnBackInvokedCallback.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.app.Activity;
+import android.app.Dialog;
+
+/**
+ * Interface for applications to register back invocation callbacks. This allows the client
+ * to customize various back behaviors by overriding the corresponding callback methods.
+ *
+ * Callback instances can be added to and removed from {@link OnBackInvokedDispatcher}, held
+ * by classes that implement {@link OnBackInvokedDispatcherOwner} (such as {@link Activity},
+ * {@link Dialog} and {@link View}).
+ *
+ * Under the hood callbacks are registered at window level. When back is triggered,
+ * callbacks on the in-focus window are invoked in reverse order in which they are added
+ * within the same priority. Between different pirorities, callbacks with higher priority
+ * are invoked first.
+ *
+ * See {@link OnBackInvokedDispatcher#registerOnBackInvokedCallback(OnBackInvokedCallback, int)}
+ * for specifying callback priority.
+ */
+public interface OnBackInvokedCallback {
+ /**
+ * Called when a back gesture has been started, or back button has been pressed down.
+ *
+ * @hide
+ */
+ default void onBackStarted() { };
+
+ /**
+ * Called on back gesture progress.
+ *
+ * @param touchX Absolute X location of the touch point.
+ * @param touchY Absolute Y location of the touch point.
+ * @param progress Value between 0 and 1 on how far along the back gesture is.
+ *
+ * @hide
+ */
+ // TODO(b/210539672): combine back progress params into BackEvent.
+ default void onBackProgressed(int touchX, int touchY, float progress) { };
+
+ /**
+ * Called when a back gesture or back button press has been cancelled.
+ *
+ * @hide
+ */
+ default void onBackCancelled() { };
+
+ /**
+ * Called when a back gesture has been completed and committed, or back button pressed
+ * has been released and committed.
+ */
+ default void onBackInvoked() { };
+}
diff --git a/core/java/android/view/OnBackInvokedDispatcher.java b/core/java/android/view/OnBackInvokedDispatcher.java
new file mode 100644
index 000000000000..05c312b56cc7
--- /dev/null
+++ b/core/java/android/view/OnBackInvokedDispatcher.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SuppressLint;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Dispatcher to register {@link OnBackInvokedCallback} instances for handling
+ * back invocations.
+ *
+ * It also provides interfaces to update the attributes of {@link OnBackInvokedCallback}.
+ * Attribute updates are proactively pushed to the window manager if they change the dispatch
+ * target (a.k.a. the callback to be invoked next), or its behavior.
+ */
+public abstract class OnBackInvokedDispatcher {
+ /** @hide */
+ @IntDef({
+ PRIORITY_DEFAULT,
+ PRIORITY_OVERLAY,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Priority{}
+
+ /**
+ * Priority level of {@link OnBackInvokedCallback}s for overlays such as menus and
+ * navigation drawers that should receive back dispatch before non-overlays.
+ */
+ public static final int PRIORITY_OVERLAY = 1000000;
+
+ /**
+ * Default priority level of {@link OnBackInvokedCallback}s.
+ */
+ public static final int PRIORITY_DEFAULT = 0;
+
+ /**
+ * Registers a {@link OnBackInvokedCallback}.
+ *
+ * Within the same priority level, callbacks are invoked in the reverse order in which
+ * they are registered. Higher priority callbacks are invoked before lower priority ones.
+ *
+ * @param callback The callback to be registered. If the callback instance has been already
+ * registered, the existing instance (no matter its priority) will be
+ * unregistered and registered again.
+ * @param priority The priority of the callback.
+ */
+ @SuppressLint("SamShouldBeLast")
+ public abstract void registerOnBackInvokedCallback(
+ @NonNull OnBackInvokedCallback callback, @Priority int priority);
+
+ /**
+ * Unregisters a {@link OnBackInvokedCallback}.
+ *
+ * @param callback The callback to be unregistered. Does nothing if the callback has not been
+ * registered.
+ */
+ public abstract void unregisterOnBackInvokedCallback(@NonNull OnBackInvokedCallback callback);
+}
diff --git a/core/java/android/view/OnBackInvokedDispatcherOwner.java b/core/java/android/view/OnBackInvokedDispatcherOwner.java
new file mode 100644
index 000000000000..0e14ed4cdb07
--- /dev/null
+++ b/core/java/android/view/OnBackInvokedDispatcherOwner.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.annotation.Nullable;
+
+/**
+ * A class that provides an {@link OnBackInvokedDispatcher} that allows you to register
+ * an {@link OnBackInvokedCallback} for handling the system back invocation behavior.
+ */
+public interface OnBackInvokedDispatcherOwner {
+ /**
+ * Returns the {@link OnBackInvokedDispatcher} that should dispatch the back invocation
+ * to its registered {@link OnBackInvokedCallback}s.
+ * Returns null when the root view is not attached to a window or a view tree with a decor.
+ */
+ @Nullable
+ OnBackInvokedDispatcher getOnBackInvokedDispatcher();
+}
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index b7f9be70f7ce..e751720b7f5d 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -1831,13 +1831,15 @@ public final class SurfaceControl implements Parcelable {
public float density;
public boolean secure;
public DeviceProductInfo deviceProductInfo;
+ public @Surface.Rotation int installOrientation;
@Override
public String toString() {
return "StaticDisplayInfo{isInternal=" + isInternal
+ ", density=" + density
+ ", secure=" + secure
- + ", deviceProductInfo=" + deviceProductInfo + "}";
+ + ", deviceProductInfo=" + deviceProductInfo
+ + ", installOrientation=" + installOrientation + "}";
}
@Override
@@ -1848,12 +1850,13 @@ public final class SurfaceControl implements Parcelable {
return isInternal == that.isInternal
&& density == that.density
&& secure == that.secure
- && Objects.equals(deviceProductInfo, that.deviceProductInfo);
+ && Objects.equals(deviceProductInfo, that.deviceProductInfo)
+ && installOrientation == that.installOrientation;
}
@Override
public int hashCode() {
- return Objects.hash(isInternal, density, secure, deviceProductInfo);
+ return Objects.hash(isInternal, density, secure, deviceProductInfo, installOrientation);
}
}
diff --git a/core/java/android/view/SurfaceControlFpsListener.java b/core/java/android/view/SurfaceControlFpsListener.java
deleted file mode 100644
index 20a511a090b5..000000000000
--- a/core/java/android/view/SurfaceControlFpsListener.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * 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 android.view;
-
-import android.annotation.NonNull;
-
-/**
- * Listener for sampling the frames per second for a SurfaceControl and its children.
- * This should only be used by a system component that needs to listen to a SurfaceControl's
- * tree's FPS when it is not actively submitting transactions for that SurfaceControl.
- * Otherwise, ASurfaceTransaction_OnComplete callbacks should be used.
- *
- * @hide
- */
-public abstract class SurfaceControlFpsListener {
- private long mNativeListener;
-
- public SurfaceControlFpsListener() {
- mNativeListener = nativeCreate(this);
- }
-
- protected void destroy() {
- if (mNativeListener == 0) {
- return;
- }
- unregister();
- nativeDestroy(mNativeListener);
- mNativeListener = 0;
- }
-
- @Override
- protected void finalize() throws Throwable {
- try {
- destroy();
- } finally {
- super.finalize();
- }
- }
-
- /**
- * Reports the fps from the registered SurfaceControl
- */
- public abstract void onFpsReported(float fps);
-
- /**
- * Registers the sampling listener for a particular task ID
- */
- public void register(int taskId) {
- if (mNativeListener == 0) {
- return;
- }
-
- nativeRegister(mNativeListener, taskId);
- }
-
- /**
- * Unregisters the sampling listener.
- */
- public void unregister() {
- if (mNativeListener == 0) {
- return;
- }
- nativeUnregister(mNativeListener);
- }
-
- /**
- * Dispatch the collected sample.
- *
- * Called from native code on a binder thread.
- */
- private static void dispatchOnFpsReported(
- @NonNull SurfaceControlFpsListener listener, float fps) {
- listener.onFpsReported(fps);
- }
-
- private static native long nativeCreate(SurfaceControlFpsListener thiz);
- private static native void nativeDestroy(long ptr);
- private static native void nativeRegister(long ptr, int taskId);
- private static native void nativeUnregister(long ptr);
-}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 41a31e1011f2..93fdee07b58e 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -834,7 +834,7 @@ import java.util.function.Predicate;
*/
@UiThread
public class View implements Drawable.Callback, KeyEvent.Callback,
- AccessibilityEventSource {
+ AccessibilityEventSource, OnBackInvokedDispatcherOwner {
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private static final boolean DBG = false;
@@ -4734,15 +4734,18 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
WindowInsetsAnimation.Callback mWindowInsetsAnimationCallback;
/**
- * This lives here since it's only valid for interactive views. This list is null until the
- * first use.
+ * This lives here since it's only valid for interactive views. This list is null
+ * until its first use.
*/
private List<Rect> mSystemGestureExclusionRects = null;
+ private List<Rect> mKeepClearRects = null;
+ private boolean mPreferKeepClear = false;
/**
- * Used to track {@link #mSystemGestureExclusionRects}
+ * Used to track {@link #mSystemGestureExclusionRects} and {@link #mKeepClearRects}
*/
public RenderNode.PositionUpdateListener mPositionUpdateListener;
+ private Runnable mPositionChangedUpdate;
/**
* Allows the application to implement custom scroll capture support.
@@ -6028,6 +6031,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
case R.styleable.View_clipToOutline:
setClipToOutline(a.getBoolean(attr, false));
break;
+ case R.styleable.View_preferKeepClear:
+ setPreferKeepClear(a.getBoolean(attr, false));
+ break;
}
}
@@ -11665,37 +11671,49 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
} else {
info.mSystemGestureExclusionRects = new ArrayList<>(rects);
}
- if (rects.isEmpty()) {
+
+ updatePositionUpdateListener();
+ postUpdate(this::updateSystemGestureExclusionRects);
+ }
+
+ private void updatePositionUpdateListener() {
+ final ListenerInfo info = getListenerInfo();
+ if (getSystemGestureExclusionRects().isEmpty()
+ && collectPreferKeepClearRects().isEmpty()) {
if (info.mPositionUpdateListener != null) {
mRenderNode.removePositionUpdateListener(info.mPositionUpdateListener);
+ info.mPositionChangedUpdate = null;
}
} else {
if (info.mPositionUpdateListener == null) {
+ info.mPositionChangedUpdate = () -> {
+ updateSystemGestureExclusionRects();
+ updateKeepClearRects();
+ };
info.mPositionUpdateListener = new RenderNode.PositionUpdateListener() {
@Override
public void positionChanged(long n, int l, int t, int r, int b) {
- postUpdateSystemGestureExclusionRects();
+ postUpdate(info.mPositionChangedUpdate);
}
@Override
public void positionLost(long frameNumber) {
- postUpdateSystemGestureExclusionRects();
+ postUpdate(info.mPositionChangedUpdate);
}
};
mRenderNode.addPositionUpdateListener(info.mPositionUpdateListener);
}
}
- postUpdateSystemGestureExclusionRects();
}
/**
* WARNING: this can be called by a hwui worker thread, not just the UI thread!
*/
- void postUpdateSystemGestureExclusionRects() {
+ private void postUpdate(Runnable r) {
// Potentially racey from a background thread. It's ok if it's not perfect.
final Handler h = getHandler();
if (h != null) {
- h.postAtFrontOfQueue(this::updateSystemGestureExclusionRects);
+ h.postAtFrontOfQueue(r);
}
}
@@ -11727,6 +11745,106 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
/**
+ * Set a preference to keep the bounds of this view clear from floating windows above this
+ * view's window. This informs the system that the view is considered a vital area for the
+ * user and that ideally it should not be covered. Setting this is only appropriate for UI
+ * where the user would likely take action to uncover it.
+ * <p>
+ * The system will try to respect this, but when not possible will ignore it.
+ * <p>
+ * @see #setPreferKeepClearRects
+ * @see #isPreferKeepClear
+ * @attr ref android.R.styleable#View_preferKeepClear
+ */
+ public final void setPreferKeepClear(boolean preferKeepClear) {
+ getListenerInfo().mPreferKeepClear = preferKeepClear;
+ updatePositionUpdateListener();
+ postUpdate(this::updateKeepClearRects);
+ }
+
+ /**
+ * Retrieve the preference for this view to be kept clear. This is set either by
+ * {@link #setPreferKeepClear} or via the attribute android.R.styleable#View_preferKeepClear.
+ * <p>
+ * If this is {@code true}, the system will ignore the Rects set by
+ * {@link #setPreferKeepClearRects} and try to keep the whole view clear.
+ * <p>
+ * @see #setPreferKeepClear
+ * @attr ref android.R.styleable#View_preferKeepClear
+ */
+ public final boolean isPreferKeepClear() {
+ return mListenerInfo != null && mListenerInfo.mPreferKeepClear;
+ }
+
+ /**
+ * Set a preference to keep the provided rects clear from floating windows above this
+ * view's window. This informs the system that these rects are considered vital areas for the
+ * user and that ideally they should not be covered. Setting this is only appropriate for UI
+ * where the user would likely take action to uncover it.
+ * <p>
+ * If the whole view is preferred to be clear ({@link #isPreferKeepClear}), the rects set here
+ * will be ignored.
+ * <p>
+ * The system will try to respect this preference, but when not possible will ignore it.
+ * <p>
+ * @see #setPreferKeepClear
+ * @see #getPreferKeepClearRects
+ */
+ public final void setPreferKeepClearRects(@NonNull List<Rect> rects) {
+ final ListenerInfo info = getListenerInfo();
+ if (info.mKeepClearRects != null) {
+ info.mKeepClearRects.clear();
+ info.mKeepClearRects.addAll(rects);
+ } else {
+ info.mKeepClearRects = new ArrayList<>(rects);
+ }
+ updatePositionUpdateListener();
+ postUpdate(this::updateKeepClearRects);
+ }
+
+ /**
+ * @return the list of rects, set by {@link #setPreferKeepClearRects}.
+ *
+ * @see #setPreferKeepClearRects
+ */
+ @NonNull
+ public final List<Rect> getPreferKeepClearRects() {
+ final ListenerInfo info = mListenerInfo;
+ if (info != null && info.mKeepClearRects != null) {
+ return new ArrayList(info.mKeepClearRects);
+ }
+
+ return Collections.emptyList();
+ }
+
+ void updateKeepClearRects() {
+ final AttachInfo ai = mAttachInfo;
+ if (ai != null) {
+ ai.mViewRootImpl.updateKeepClearRectsForView(this);
+ }
+ }
+
+ /**
+ * Retrieve the list of areas within this view's post-layout coordinate space which the
+ * system will try to not cover with other floating elements, like the pip window.
+ */
+ @NonNull
+ List<Rect> collectPreferKeepClearRects() {
+ final ListenerInfo info = mListenerInfo;
+ if (info != null) {
+ final List<Rect> list = new ArrayList();
+ if (info.mPreferKeepClear) {
+ list.add(new Rect(0, 0, getWidth(), getHeight()));
+ } else if (info.mKeepClearRects != null) {
+ list.addAll(info.mKeepClearRects);
+ }
+ return list;
+ }
+
+ return Collections.emptyList();
+ }
+
+ /**
* Compute the view's coordinate within the surface.
*
* <p>Computes the coordinates of this view in its surface. The argument
@@ -15120,7 +15238,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
notifyAppearedOrDisappearedForContentCaptureIfNeeded(isVisible);
if (!getSystemGestureExclusionRects().isEmpty()) {
- postUpdateSystemGestureExclusionRects();
+ postUpdate(this::updateSystemGestureExclusionRects);
+ }
+
+ if (!collectPreferKeepClearRects().isEmpty()) {
+ postUpdate(this::updateKeepClearRects);
}
}
}
@@ -31276,4 +31398,23 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
return null;
}
+
+ /**
+ * Returns the {@link OnBackInvokedDispatcher} instance of the window this view is attached to.
+ *
+ * @return The {@link OnBackInvokedDispatcher} or {@code null} if the view is neither attached
+ * to a window or a view tree with a decor.
+ */
+ @Nullable
+ public OnBackInvokedDispatcher getOnBackInvokedDispatcher() {
+ ViewParent parent = getParent();
+ if (parent instanceof View) {
+ return ((View) parent).getOnBackInvokedDispatcher();
+ } else if (parent instanceof ViewRootImpl) {
+ // Get the fallback dispatcher on {@link ViewRootImpl} if the view tree doesn't have
+ // a {@link com.android.internal.policy.DecorView}.
+ return ((ViewRootImpl) parent).getOnBackInvokedDispatcher();
+ }
+ return null;
+ }
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 74129312b41b..97b5a3181dbf 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -192,6 +192,7 @@ import android.view.contentcapture.MainContentCaptureSession;
import android.view.inputmethod.InputMethodManager;
import android.widget.Scroller;
import android.window.ClientWindowFrames;
+import android.window.WindowOnBackInvokedDispatcher;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
@@ -309,6 +310,12 @@ public final class ViewRootImpl implements ViewParent,
private @SurfaceControl.BufferTransform
int mPreviousTransformHint = SurfaceControl.BUFFER_TRANSFORM_IDENTITY;
/**
+ * The fallback {@link OnBackInvokedDispatcher} when the window doesn't have a decor view.
+ */
+ private WindowOnBackInvokedDispatcher mFallbackOnBackInvokedDispatcher =
+ new WindowOnBackInvokedDispatcher();
+
+ /**
* Callback for notifying about global configuration changes.
*/
public interface ConfigChangedCallback {
@@ -391,6 +398,8 @@ public final class ViewRootImpl implements ViewParent,
final DisplayManager mDisplayManager;
final String mBasePackageName;
+ private @Surface.Rotation int mDisplayInstallOrientation;
+
final int[] mTmpLocation = new int[2];
final TypedValue mTmpValue = new TypedValue();
@@ -742,7 +751,10 @@ public final class ViewRootImpl implements ViewParent,
return mImeFocusController;
}
- private final GestureExclusionTracker mGestureExclusionTracker = new GestureExclusionTracker();
+ private final ViewRootRectTracker mGestureExclusionTracker =
+ new ViewRootRectTracker(v -> v.getSystemGestureExclusionRects());
+ private final ViewRootRectTracker mKeepClearRectsTracker =
+ new ViewRootRectTracker(v -> v.collectPreferKeepClearRects());
private IAccessibilityEmbeddedConnection mAccessibilityEmbeddedConnection;
@@ -871,6 +883,7 @@ public final class ViewRootImpl implements ViewParent,
mFastScrollSoundEffectsEnabled = audioManager.areNavigationRepeatSoundEffectsEnabled();
mScrollCaptureRequestTimeout = SCROLL_CAPTURE_REQUEST_TIMEOUT_MILLIS;
+ mFallbackOnBackInvokedDispatcher.attachToWindow(mWindowSession, mWindow);
}
public static void addFirstDrawHandler(Runnable callback) {
@@ -1017,6 +1030,7 @@ public final class ViewRootImpl implements ViewParent,
mView = view;
mAttachInfo.mDisplayState = mDisplay.getState();
+ mDisplayInstallOrientation = mDisplay.getInstallOrientation();
mViewLayoutDirectionInitial = mView.getRawLayoutDirection();
mFallbackEventHandler.setView(view);
mWindowAttributes.copyFrom(attrs);
@@ -1120,6 +1134,9 @@ public final class ViewRootImpl implements ViewParent,
if (pendingInsetsController != null) {
pendingInsetsController.replayAndAttach(mInsetsController);
}
+ ((RootViewSurfaceTaker) mView)
+ .provideWindowOnBackInvokedDispatcher()
+ .attachToWindow(mWindowSession, mWindow);
}
try {
@@ -4756,7 +4773,7 @@ public final class ViewRootImpl implements ViewParent,
* the root's view hierarchy.
*/
public void setRootSystemGestureExclusionRects(@NonNull List<Rect> rects) {
- mGestureExclusionTracker.setRootSystemGestureExclusionRects(rects);
+ mGestureExclusionTracker.setRootRects(rects);
mHandler.sendEmptyMessage(MSG_SYSTEM_GESTURE_EXCLUSION_CHANGED);
}
@@ -4766,7 +4783,26 @@ public final class ViewRootImpl implements ViewParent,
*/
@NonNull
public List<Rect> getRootSystemGestureExclusionRects() {
- return mGestureExclusionTracker.getRootSystemGestureExclusionRects();
+ return mGestureExclusionTracker.getRootRects();
+ }
+
+ /**
+ * Called from View when the position listener is triggered
+ */
+ void updateKeepClearRectsForView(View view) {
+ mKeepClearRectsTracker.updateRectsForView(view);
+ mHandler.sendEmptyMessage(MSG_KEEP_CLEAR_RECTS_CHANGED);
+ }
+
+ void keepClearRectsChanged() {
+ final List<Rect> rectsForWindowManager = mKeepClearRectsTracker.computeChangedRects();
+ if (rectsForWindowManager != null && mView != null) {
+ try {
+ mWindowSession.reportKeepClearAreasChanged(mWindow, rectsForWindowManager);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
/**
@@ -5262,6 +5298,7 @@ public final class ViewRootImpl implements ViewParent,
private static final int MSG_HIDE_INSETS = 35;
private static final int MSG_REQUEST_SCROLL_CAPTURE = 36;
private static final int MSG_WINDOW_TOUCH_MODE_CHANGED = 37;
+ private static final int MSG_KEEP_CLEAR_RECTS_CHANGED = 38;
final class ViewRootHandler extends Handler {
@@ -5330,6 +5367,8 @@ public final class ViewRootImpl implements ViewParent,
return "MSG_HIDE_INSETS";
case MSG_WINDOW_TOUCH_MODE_CHANGED:
return "MSG_WINDOW_TOUCH_MODE_CHANGED";
+ case MSG_KEEP_CLEAR_RECTS_CHANGED:
+ return "MSG_KEEP_CLEAR_RECTS_CHANGED";
}
return super.getMessageName(message);
}
@@ -5553,7 +5592,10 @@ public final class ViewRootImpl implements ViewParent,
} break;
case MSG_SYSTEM_GESTURE_EXCLUSION_CHANGED: {
systemGestureExclusionChanged();
- } break;
+ } break;
+ case MSG_KEEP_CLEAR_RECTS_CHANGED: {
+ keepClearRectsChanged();
+ } break;
case MSG_REQUEST_SCROLL_CAPTURE:
handleScrollCaptureRequest((IScrollCaptureResponseListener) msg.obj);
break;
@@ -7898,6 +7940,10 @@ public final class ViewRootImpl implements ViewParent,
mTmpFrames, mPendingMergedConfiguration, mSurfaceControl, mTempInsets,
mTempControls, mSurfaceSize);
+ final int transformHint = SurfaceControl.rotationToBufferTransform(
+ (mDisplayInstallOrientation + mDisplay.getRotation()) % 4);
+ mSurfaceControl.setTransformHint(transformHint);
+
if (mAttachInfo.mContentCaptureManager != null) {
MainContentCaptureSession mainSession = mAttachInfo.mContentCaptureManager
.getMainContentCaptureSession();
@@ -7916,7 +7962,6 @@ public final class ViewRootImpl implements ViewParent,
mAttachInfo.mThreadedRenderer.setSurfaceControl(mSurfaceControl);
mAttachInfo.mThreadedRenderer.setBlastBufferQueue(mBlastBufferQueue);
}
- int transformHint = mSurfaceControl.getTransformHint();
if (mPreviousTransformHint != transformHint) {
mPreviousTransformHint = transformHint;
dispatchTransformHintChanged(transformHint);
@@ -10634,7 +10679,7 @@ public final class ViewRootImpl implements ViewParent,
}
mBLASTDrawConsumer = consume;
return true;
- }
+ }
boolean wasRelayoutRequested() {
return mRelayoutRequested;
@@ -10644,4 +10689,16 @@ public final class ViewRootImpl implements ViewParent,
mForceNextWindowRelayout = true;
scheduleTraversals();
}
+
+ /**
+ * Returns the {@link OnBackInvokedDispatcher} on the decor view if one exists, or the
+ * fallback {@link OnBackInvokedDispatcher} instance.
+ */
+ @Nullable
+ public OnBackInvokedDispatcher getOnBackInvokedDispatcher() {
+ if (mView instanceof RootViewSurfaceTaker) {
+ return ((RootViewSurfaceTaker) mView).provideWindowOnBackInvokedDispatcher();
+ }
+ return mFallbackOnBackInvokedDispatcher;
+ }
}
diff --git a/core/java/android/view/ViewRootRectTracker.java b/core/java/android/view/ViewRootRectTracker.java
new file mode 100644
index 000000000000..fd9cc1920b39
--- /dev/null
+++ b/core/java/android/view/ViewRootRectTracker.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.Rect;
+
+import com.android.internal.util.Preconditions;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.function.Function;
+
+/**
+ * Abstract class to track a collection of rects reported by the views under the same
+ * {@link ViewRootImpl}.
+ */
+class ViewRootRectTracker {
+ private final Function<View, List<Rect>> mRectCollector;
+ private boolean mViewsChanged = false;
+ private boolean mRootRectsChanged = false;
+ private List<Rect> mRootRects = Collections.emptyList();
+ private List<ViewInfo> mViewInfos = new ArrayList<>();
+ private List<Rect> mRects = Collections.emptyList();
+
+ /**
+ * @param rectCollector given a view returns a list of the rects of interest for this
+ * ViewRootRectTracker
+ */
+ ViewRootRectTracker(Function<View, List<Rect>> rectCollector) {
+ mRectCollector = rectCollector;
+ }
+
+ public void updateRectsForView(@NonNull View view) {
+ boolean found = false;
+ final Iterator<ViewInfo> i = mViewInfos.iterator();
+ while (i.hasNext()) {
+ final ViewInfo info = i.next();
+ final View v = info.getView();
+ if (v == null || !v.isAttachedToWindow() || !v.isAggregatedVisible()) {
+ mViewsChanged = true;
+ i.remove();
+ continue;
+ }
+ if (v == view) {
+ found = true;
+ info.mDirty = true;
+ break;
+ }
+ }
+ if (!found && view.isAttachedToWindow()) {
+ mViewInfos.add(new ViewInfo(view));
+ mViewsChanged = true;
+ }
+ }
+
+ /**
+ * @return all visible rects from all views in the global (root) coordinate system
+ */
+ @Nullable
+ public List<Rect> computeChangedRects() {
+ boolean changed = mRootRectsChanged;
+ final Iterator<ViewInfo> i = mViewInfos.iterator();
+ final List<Rect> rects = new ArrayList<>(mRootRects);
+ while (i.hasNext()) {
+ final ViewInfo info = i.next();
+ switch (info.update()) {
+ case ViewInfo.CHANGED:
+ changed = true;
+ // Deliberate fall-through
+ case ViewInfo.UNCHANGED:
+ rects.addAll(info.mRects);
+ break;
+ case ViewInfo.GONE:
+ mViewsChanged = true;
+ i.remove();
+ break;
+ }
+ }
+ if (changed || mViewsChanged) {
+ mViewsChanged = false;
+ mRootRectsChanged = false;
+ if (!mRects.equals(rects)) {
+ mRects = rects;
+ return rects;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Sets rects defined in the global (root) coordinate system, i.e. not for a specific view.
+ */
+ public void setRootRects(@NonNull List<Rect> rects) {
+ Preconditions.checkNotNull(rects, "rects must not be null");
+ mRootRects = rects;
+ mRootRectsChanged = true;
+ }
+
+ @NonNull
+ public List<Rect> getRootRects() {
+ return mRootRects;
+ }
+
+ @NonNull
+ private List<Rect> getTrackedRectsForView(@NonNull View v) {
+ final List<Rect> rects = mRectCollector.apply(v);
+ return rects == null ? Collections.emptyList() : rects;
+ }
+
+ private class ViewInfo {
+ public static final int CHANGED = 0;
+ public static final int UNCHANGED = 1;
+ public static final int GONE = 2;
+
+ private final WeakReference<View> mView;
+ boolean mDirty = true;
+ List<Rect> mRects = Collections.emptyList();
+
+ ViewInfo(View view) {
+ mView = new WeakReference<>(view);
+ }
+
+ public View getView() {
+ return mView.get();
+ }
+
+ public int update() {
+ final View view = getView();
+ if (view == null || !view.isAttachedToWindow()
+ || !view.isAggregatedVisible()) return GONE;
+ final List<Rect> localRects = getTrackedRectsForView(view);
+ final List<Rect> newRects = new ArrayList<>(localRects.size());
+ for (Rect src : localRects) {
+ Rect mappedRect = new Rect(src);
+ ViewParent p = view.getParent();
+ if (p != null && p.getChildVisibleRect(view, mappedRect, null)) {
+ newRects.add(mappedRect);
+ }
+ }
+
+ if (mRects.equals(localRects)) return UNCHANGED;
+ mRects = newRects;
+ return CHANGED;
+ }
+ }
+}
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 5be3a57e8527..ca7f90080c6c 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -118,6 +118,7 @@ import android.view.WindowInsets.Side.InsetsSide;
import android.view.WindowInsets.Type;
import android.view.WindowInsets.Type.InsetsType;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.window.TaskFpsCallback;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -4858,4 +4859,31 @@ public interface WindowManager extends ViewManager {
default boolean isTaskSnapshotSupported() {
return false;
}
+
+ /**
+ * Registers the frame rate per second count callback for one given task ID.
+ * Each callback can only register for receiving FPS callback for one task id until unregister
+ * is called. If there's no task associated with the given task id,
+ * {@link IllegalArgumentException} will be thrown. If a task id destroyed after a callback is
+ * registered, the registered callback will not be unregistered until
+ * {@link #unregisterTaskFpsCallback(TaskFpsCallback))} is called
+ * @param taskId task id of the task.
+ * @param callback callback to be registered.
+ *
+ * @hide
+ */
+ @SystemApi
+ default void registerTaskFpsCallback(@IntRange(from = 0) int taskId,
+ @NonNull TaskFpsCallback callback) {}
+
+ /**
+ * Unregisters the frame rate per second count callback which was registered with
+ * {@link #registerTaskFpsCallback(int,TaskFpsCallback)}.
+ *
+ * @param callback callback to be unregistered.
+ *
+ * @hide
+ */
+ @SystemApi
+ default void unregisterTaskFpsCallback(@NonNull TaskFpsCallback callback) {}
}
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index dd8041684c78..c16703ef50ef 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -24,6 +24,7 @@ import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
import static android.window.WindowProviderService.isWindowProviderService;
import android.annotation.CallbackExecutor;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UiContext;
@@ -37,6 +38,7 @@ import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.StrictMode;
+import android.window.TaskFpsCallback;
import android.window.WindowContext;
import android.window.WindowProvider;
@@ -419,4 +421,22 @@ public final class WindowManagerImpl implements WindowManager {
}
return false;
}
+
+ @Override
+ public void registerTaskFpsCallback(@IntRange(from = 0) int taskId, TaskFpsCallback callback) {
+ try {
+ WindowManagerGlobal.getWindowManagerService().registerTaskFpsCallback(
+ taskId, callback.getListener());
+ } catch (RemoteException e) {
+ }
+ }
+
+ @Override
+ public void unregisterTaskFpsCallback(TaskFpsCallback callback) {
+ try {
+ WindowManagerGlobal.getWindowManagerService().unregisterTaskFpsCallback(
+ callback.getListener());
+ } catch (RemoteException e) {
+ }
+ }
}
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index 5176f9bb0f93..998498b0799a 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -28,6 +28,7 @@ import android.os.RemoteException;
import android.util.Log;
import android.util.MergedConfiguration;
import android.window.ClientWindowFrames;
+import android.window.IOnBackInvokedCallback;
import java.util.HashMap;
import java.util.Objects;
@@ -459,6 +460,11 @@ public class WindowlessWindowManager implements IWindowSession {
}
@Override
+ public void reportKeepClearAreasChanged(android.view.IWindow window,
+ java.util.List<android.graphics.Rect> exclusionRects) {
+ }
+
+ @Override
public void grantInputChannel(int displayId, SurfaceControl surface, IWindow window,
IBinder hostInputToken, int flags, int privateFlags, int type,
InputChannel outInputChannel) {
@@ -496,6 +502,10 @@ public class WindowlessWindowManager implements IWindowSession {
}
@Override
+ public void setOnBackInvokedCallback(IWindow iWindow,
+ IOnBackInvokedCallback iOnBackInvokedCallback) throws RemoteException { }
+
+ @Override
public boolean dropForAccessibility(IWindow window, int x, int y) {
return false;
}
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index 6ad2d9a7adb1..a427ab8fe837 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -1315,7 +1315,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
record.mContentDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
record.mBeforeText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
record.mParcelableData = parcel.readParcelable(null);
- parcel.readList(record.mText, null);
+ parcel.readList(record.mText, null, java.lang.CharSequence.class);
record.mSourceWindowId = parcel.readInt();
record.mSourceNodeId = parcel.readLong();
record.mSourceDisplayId = parcel.readInt();
diff --git a/core/java/android/view/accessibility/AccessibilityWindowInfo.java b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
index 67e6d3f2aec3..540f5dc27f7e 100644
--- a/core/java/android/view/accessibility/AccessibilityWindowInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
@@ -917,7 +917,7 @@ public final class AccessibilityWindowInfo implements Parcelable {
final int count = source.readInt();
for (int i = 0; i < count; i++) {
List<AccessibilityWindowInfo> windows = new ArrayList<>();
- source.readParcelableList(windows, loader);
+ source.readParcelableList(windows, loader, android.view.accessibility.AccessibilityWindowInfo.class);
array.put(source.readInt(), windows);
}
return array;
diff --git a/core/java/android/view/accessibility/CaptioningManager.java b/core/java/android/view/accessibility/CaptioningManager.java
index 05c74f2d0111..4f9781b6b6af 100644
--- a/core/java/android/view/accessibility/CaptioningManager.java
+++ b/core/java/android/view/accessibility/CaptioningManager.java
@@ -254,7 +254,13 @@ public class CaptioningManager {
* Returns true if system wide call captioning is enabled for this device.
*/
public boolean isCallCaptioningEnabled() {
- return mResources.getBoolean(R.bool.config_systemCaptionsServiceCallsEnabled);
+ try {
+ return mResources.getBoolean(
+ R.bool.config_systemCaptionsServiceCallsEnabled);
+ } catch (Resources.NotFoundException e) {
+ // The resource may not be defined, return false in that case
+ return false;
+ }
}
private void notifyEnabledChanged() {
diff --git a/core/java/android/view/autofill/ParcelableMap.java b/core/java/android/view/autofill/ParcelableMap.java
index d8459aa22fa1..3fa7734e56fc 100644
--- a/core/java/android/view/autofill/ParcelableMap.java
+++ b/core/java/android/view/autofill/ParcelableMap.java
@@ -56,8 +56,8 @@ class ParcelableMap extends HashMap<AutofillId, AutofillValue> implements Parcel
ParcelableMap map = new ParcelableMap(size);
for (int i = 0; i < size; i++) {
- AutofillId key = source.readParcelable(null);
- AutofillValue value = source.readParcelable(null);
+ AutofillId key = source.readParcelable(null, android.view.autofill.AutofillId.class);
+ AutofillValue value = source.readParcelable(null, android.view.autofill.AutofillValue.class);
map.put(key, value);
}
diff --git a/core/java/android/view/contentcapture/ContentCaptureCondition.java b/core/java/android/view/contentcapture/ContentCaptureCondition.java
index 027c8d20ccc6..685ea1aeaba8 100644
--- a/core/java/android/view/contentcapture/ContentCaptureCondition.java
+++ b/core/java/android/view/contentcapture/ContentCaptureCondition.java
@@ -133,7 +133,7 @@ public final class ContentCaptureCondition implements Parcelable {
@Override
public ContentCaptureCondition createFromParcel(@NonNull Parcel parcel) {
- return new ContentCaptureCondition(parcel.readParcelable(null),
+ return new ContentCaptureCondition(parcel.readParcelable(null, android.content.LocusId.class),
parcel.readInt());
}
diff --git a/core/java/android/view/contentcapture/ContentCaptureContext.java b/core/java/android/view/contentcapture/ContentCaptureContext.java
index 3bc9a967ea20..59b5286f6fc5 100644
--- a/core/java/android/view/contentcapture/ContentCaptureContext.java
+++ b/core/java/android/view/contentcapture/ContentCaptureContext.java
@@ -419,7 +419,7 @@ public final class ContentCaptureContext implements Parcelable {
final ContentCaptureContext clientContext;
if (hasClientContext) {
// Must reconstruct the client context using the Builder API
- final LocusId id = parcel.readParcelable(null);
+ final LocusId id = parcel.readParcelable(null, android.content.LocusId.class);
final Bundle extras = parcel.readBundle();
final Builder builder = new Builder(id);
if (extras != null) builder.setExtras(extras);
@@ -427,7 +427,7 @@ public final class ContentCaptureContext implements Parcelable {
} else {
clientContext = null;
}
- final ComponentName componentName = parcel.readParcelable(null);
+ final ComponentName componentName = parcel.readParcelable(null, android.content.ComponentName.class);
if (componentName == null) {
// Client-state only
return clientContext;
diff --git a/core/java/android/view/contentcapture/ContentCaptureEvent.java b/core/java/android/view/contentcapture/ContentCaptureEvent.java
index 0f4bc191fe4e..ba4176faa283 100644
--- a/core/java/android/view/contentcapture/ContentCaptureEvent.java
+++ b/core/java/android/view/contentcapture/ContentCaptureEvent.java
@@ -620,7 +620,7 @@ public final class ContentCaptureEvent implements Parcelable {
final int type = parcel.readInt();
final long eventTime = parcel.readLong();
final ContentCaptureEvent event = new ContentCaptureEvent(sessionId, type, eventTime);
- final AutofillId id = parcel.readParcelable(null);
+ final AutofillId id = parcel.readParcelable(null, android.view.autofill.AutofillId.class);
if (id != null) {
event.setAutofillId(id);
}
@@ -637,13 +637,13 @@ public final class ContentCaptureEvent implements Parcelable {
event.setParentSessionId(parcel.readInt());
}
if (type == TYPE_SESSION_STARTED || type == TYPE_CONTEXT_UPDATED) {
- event.setClientContext(parcel.readParcelable(null));
+ event.setClientContext(parcel.readParcelable(null, android.view.contentcapture.ContentCaptureContext.class));
}
if (type == TYPE_VIEW_INSETS_CHANGED) {
- event.setInsets(parcel.readParcelable(null));
+ event.setInsets(parcel.readParcelable(null, android.graphics.Insets.class));
}
if (type == TYPE_WINDOW_BOUNDS_CHANGED) {
- event.setBounds(parcel.readParcelable(null));
+ event.setBounds(parcel.readParcelable(null, android.graphics.Rect.class));
}
if (type == TYPE_VIEW_TEXT_CHANGED) {
event.setComposingIndex(parcel.readInt(), parcel.readInt());
diff --git a/core/java/android/view/contentcapture/ViewNode.java b/core/java/android/view/contentcapture/ViewNode.java
index 1b4a00f81e44..1762a5817aaf 100644
--- a/core/java/android/view/contentcapture/ViewNode.java
+++ b/core/java/android/view/contentcapture/ViewNode.java
@@ -124,10 +124,10 @@ public final class ViewNode extends AssistStructure.ViewNode {
mFlags = nodeFlags;
if ((nodeFlags & FLAGS_HAS_AUTOFILL_ID) != 0) {
- mAutofillId = parcel.readParcelable(null);
+ mAutofillId = parcel.readParcelable(null, android.view.autofill.AutofillId.class);
}
if ((nodeFlags & FLAGS_HAS_AUTOFILL_PARENT_ID) != 0) {
- mParentAutofillId = parcel.readParcelable(null);
+ mParentAutofillId = parcel.readParcelable(null, android.view.autofill.AutofillId.class);
}
if ((nodeFlags & FLAGS_HAS_TEXT) != 0) {
mText = new ViewNodeText(parcel, (nodeFlags & FLAGS_HAS_COMPLEX_TEXT) == 0);
@@ -169,7 +169,7 @@ public final class ViewNode extends AssistStructure.ViewNode {
mExtras = parcel.readBundle();
}
if ((nodeFlags & FLAGS_HAS_LOCALE_LIST) != 0) {
- mLocaleList = parcel.readParcelable(null);
+ mLocaleList = parcel.readParcelable(null, android.os.LocaleList.class);
}
if ((nodeFlags & FLAGS_HAS_MIME_TYPES) != 0) {
mReceiveContentMimeTypes = parcel.readStringArray();
@@ -196,7 +196,7 @@ public final class ViewNode extends AssistStructure.ViewNode {
mAutofillHints = parcel.readStringArray();
}
if ((nodeFlags & FLAGS_HAS_AUTOFILL_VALUE) != 0) {
- mAutofillValue = parcel.readParcelable(null);
+ mAutofillValue = parcel.readParcelable(null, android.view.autofill.AutofillValue.class);
}
if ((nodeFlags & FLAGS_HAS_AUTOFILL_OPTIONS) != 0) {
mAutofillOptions = parcel.readCharSequenceArray();
diff --git a/core/java/android/view/inputmethod/CursorAnchorInfo.java b/core/java/android/view/inputmethod/CursorAnchorInfo.java
index e3d3bfd30b9c..8600f55eee70 100644
--- a/core/java/android/view/inputmethod/CursorAnchorInfo.java
+++ b/core/java/android/view/inputmethod/CursorAnchorInfo.java
@@ -147,7 +147,7 @@ public final class CursorAnchorInfo implements Parcelable {
mInsertionMarkerTop = source.readFloat();
mInsertionMarkerBaseline = source.readFloat();
mInsertionMarkerBottom = source.readFloat();
- mCharacterBoundsArray = source.readParcelable(SparseRectFArray.class.getClassLoader());
+ mCharacterBoundsArray = source.readParcelable(SparseRectFArray.class.getClassLoader(), android.view.inputmethod.SparseRectFArray.class);
mEditorBoundsInfo = source.readTypedObject(EditorBoundsInfo.CREATOR);
mMatrixValues = source.createFloatArray();
}
diff --git a/core/java/android/view/inputmethod/EditorInfo.java b/core/java/android/view/inputmethod/EditorInfo.java
index 4cbd477d807a..09a14484095e 100644
--- a/core/java/android/view/inputmethod/EditorInfo.java
+++ b/core/java/android/view/inputmethod/EditorInfo.java
@@ -1067,7 +1067,7 @@ public class EditorInfo implements InputType, Parcelable {
res.hintText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
res.label = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
res.packageName = source.readString();
- res.autofillId = source.readParcelable(AutofillId.class.getClassLoader());
+ res.autofillId = source.readParcelable(AutofillId.class.getClassLoader(), android.view.autofill.AutofillId.class);
res.fieldId = source.readInt();
res.fieldName = source.readString();
res.extras = source.readBundle();
diff --git a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
index e1e175512edc..70279cc8e845 100644
--- a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
+++ b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
@@ -490,7 +490,7 @@ public final class InlineSuggestionsRequest implements Parcelable {
boolean clientSupported = (flg & 0x200) != 0;
int maxSuggestionCount = in.readInt();
List<InlinePresentationSpec> inlinePresentationSpecs = new ArrayList<>();
- in.readParcelableList(inlinePresentationSpecs, InlinePresentationSpec.class.getClassLoader());
+ in.readParcelableList(inlinePresentationSpecs, InlinePresentationSpec.class.getClassLoader(), android.widget.inline.InlinePresentationSpec.class);
String hostPackageName = in.readString();
LocaleList supportedLocales = (LocaleList) in.readTypedObject(LocaleList.CREATOR);
Bundle extras = in.readBundle();
diff --git a/core/java/android/view/inputmethod/InlineSuggestionsResponse.java b/core/java/android/view/inputmethod/InlineSuggestionsResponse.java
index b393c67d7876..532fc85dcc44 100644
--- a/core/java/android/view/inputmethod/InlineSuggestionsResponse.java
+++ b/core/java/android/view/inputmethod/InlineSuggestionsResponse.java
@@ -170,7 +170,7 @@ public final class InlineSuggestionsResponse implements Parcelable {
// static FieldType unparcelFieldName(Parcel in) { ... }
List<InlineSuggestion> inlineSuggestions = new ArrayList<>();
- in.readParcelableList(inlineSuggestions, InlineSuggestion.class.getClassLoader());
+ in.readParcelableList(inlineSuggestions, InlineSuggestion.class.getClassLoader(), android.view.inputmethod.InlineSuggestion.class);
this.mInlineSuggestions = inlineSuggestions;
com.android.internal.util.AnnotationValidations.validate(
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 6fc246eb2514..6a22023dc9da 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -2139,7 +2139,7 @@ public final class InputMethodManager {
view.onInputConnectionOpenedInternal(ic, tba, icHandler);
final ViewRootImpl viewRoot = view.getViewRootImpl();
if (viewRoot != null) {
- viewRoot.getHandwritingInitiator().onInputConnectionCreated(view, tba);
+ viewRoot.getHandwritingInitiator().onInputConnectionCreated(view);
}
}
diff --git a/core/java/android/view/textclassifier/ConversationAction.java b/core/java/android/view/textclassifier/ConversationAction.java
index bf0409dfc919..a4a5a1ed0ac9 100644
--- a/core/java/android/view/textclassifier/ConversationAction.java
+++ b/core/java/android/view/textclassifier/ConversationAction.java
@@ -141,7 +141,7 @@ public final class ConversationAction implements Parcelable {
private ConversationAction(Parcel in) {
mType = in.readString();
- mAction = in.readParcelable(null);
+ mAction = in.readParcelable(null, android.app.RemoteAction.class);
mTextReply = in.readCharSequence();
mScore = in.readFloat();
mExtras = in.readBundle();
diff --git a/core/java/android/view/textclassifier/ConversationActions.java b/core/java/android/view/textclassifier/ConversationActions.java
index 6ad5cb913553..7a6a3cd026fd 100644
--- a/core/java/android/view/textclassifier/ConversationActions.java
+++ b/core/java/android/view/textclassifier/ConversationActions.java
@@ -149,7 +149,7 @@ public final class ConversationActions implements Parcelable {
}
private Message(Parcel in) {
- mAuthor = in.readParcelable(null);
+ mAuthor = in.readParcelable(null, android.app.Person.class);
mReferenceTime =
in.readInt() == 0
? null
@@ -331,13 +331,13 @@ public final class ConversationActions implements Parcelable {
private static Request readFromParcel(Parcel in) {
List<Message> conversation = new ArrayList<>();
- in.readParcelableList(conversation, null);
- TextClassifier.EntityConfig typeConfig = in.readParcelable(null);
+ in.readParcelableList(conversation, null, android.view.textclassifier.ConversationActions.Message.class);
+ TextClassifier.EntityConfig typeConfig = in.readParcelable(null, android.view.textclassifier.TextClassifier.EntityConfig.class);
int maxSuggestions = in.readInt();
List<String> hints = new ArrayList<>();
in.readStringList(hints);
Bundle extras = in.readBundle();
- SystemTextClassifierMetadata systemTcMetadata = in.readParcelable(null);
+ SystemTextClassifierMetadata systemTcMetadata = in.readParcelable(null, android.view.textclassifier.SystemTextClassifierMetadata.class);
Request request = new Request(
conversation,
diff --git a/core/java/android/view/textclassifier/SelectionEvent.java b/core/java/android/view/textclassifier/SelectionEvent.java
index 858825b1d5ac..b34701082b80 100644
--- a/core/java/android/view/textclassifier/SelectionEvent.java
+++ b/core/java/android/view/textclassifier/SelectionEvent.java
@@ -172,7 +172,7 @@ public final class SelectionEvent implements Parcelable {
mEnd = in.readInt();
mSmartStart = in.readInt();
mSmartEnd = in.readInt();
- mSystemTcMetadata = in.readParcelable(null);
+ mSystemTcMetadata = in.readParcelable(null, android.view.textclassifier.SystemTextClassifierMetadata.class);
}
@Override
diff --git a/core/java/android/view/textclassifier/TextClassification.java b/core/java/android/view/textclassifier/TextClassification.java
index 7db35d4bf8b5..8b04d35734ec 100644
--- a/core/java/android/view/textclassifier/TextClassification.java
+++ b/core/java/android/view/textclassifier/TextClassification.java
@@ -713,12 +713,12 @@ public final class TextClassification implements Parcelable {
final CharSequence text = in.readCharSequence();
final int startIndex = in.readInt();
final int endIndex = in.readInt();
- final LocaleList defaultLocales = in.readParcelable(null);
+ final LocaleList defaultLocales = in.readParcelable(null, android.os.LocaleList.class);
final String referenceTimeString = in.readString();
final ZonedDateTime referenceTime = referenceTimeString == null
? null : ZonedDateTime.parse(referenceTimeString);
final Bundle extras = in.readBundle();
- final SystemTextClassifierMetadata systemTcMetadata = in.readParcelable(null);
+ final SystemTextClassifierMetadata systemTcMetadata = in.readParcelable(null, android.view.textclassifier.SystemTextClassifierMetadata.class);
final Request request = new Request(text, startIndex, endIndex,
defaultLocales, referenceTime, extras);
diff --git a/core/java/android/view/textclassifier/TextClassificationContext.java b/core/java/android/view/textclassifier/TextClassificationContext.java
index 5d5683f7110e..3a50809ea8b4 100644
--- a/core/java/android/view/textclassifier/TextClassificationContext.java
+++ b/core/java/android/view/textclassifier/TextClassificationContext.java
@@ -159,7 +159,7 @@ public final class TextClassificationContext implements Parcelable {
mPackageName = in.readString();
mWidgetType = in.readString();
mWidgetVersion = in.readString();
- mSystemTcMetadata = in.readParcelable(null);
+ mSystemTcMetadata = in.readParcelable(null, android.view.textclassifier.SystemTextClassifierMetadata.class);
}
public static final @android.annotation.NonNull Parcelable.Creator<TextClassificationContext> CREATOR =
diff --git a/core/java/android/view/textclassifier/TextClassifierEvent.java b/core/java/android/view/textclassifier/TextClassifierEvent.java
index 90667cf54f93..195565c5bc09 100644
--- a/core/java/android/view/textclassifier/TextClassifierEvent.java
+++ b/core/java/android/view/textclassifier/TextClassifierEvent.java
@@ -189,7 +189,7 @@ public abstract class TextClassifierEvent implements Parcelable {
mEventCategory = in.readInt();
mEventType = in.readInt();
mEntityTypes = in.readStringArray();
- mEventContext = in.readParcelable(null);
+ mEventContext = in.readParcelable(null, android.view.textclassifier.TextClassificationContext.class);
mResultId = in.readString();
mEventIndex = in.readInt();
int scoresLength = in.readInt();
diff --git a/core/java/android/view/textclassifier/TextLanguage.java b/core/java/android/view/textclassifier/TextLanguage.java
index 604979b1ac78..67167c6d3e65 100644
--- a/core/java/android/view/textclassifier/TextLanguage.java
+++ b/core/java/android/view/textclassifier/TextLanguage.java
@@ -295,7 +295,7 @@ public final class TextLanguage implements Parcelable {
private static Request readFromParcel(Parcel in) {
final CharSequence text = in.readCharSequence();
final Bundle extra = in.readBundle();
- final SystemTextClassifierMetadata systemTcMetadata = in.readParcelable(null);
+ final SystemTextClassifierMetadata systemTcMetadata = in.readParcelable(null, android.view.textclassifier.SystemTextClassifierMetadata.class);
final Request request = new Request(text, extra);
request.setSystemTextClassifierMetadata(systemTcMetadata);
diff --git a/core/java/android/view/textclassifier/TextLinks.java b/core/java/android/view/textclassifier/TextLinks.java
index dea3a9010b18..445e9ecff54f 100644
--- a/core/java/android/view/textclassifier/TextLinks.java
+++ b/core/java/android/view/textclassifier/TextLinks.java
@@ -558,13 +558,13 @@ public final class TextLinks implements Parcelable {
private static Request readFromParcel(Parcel in) {
final String text = in.readString();
- final LocaleList defaultLocales = in.readParcelable(null);
- final EntityConfig entityConfig = in.readParcelable(null);
+ final LocaleList defaultLocales = in.readParcelable(null, android.os.LocaleList.class);
+ final EntityConfig entityConfig = in.readParcelable(null, android.view.textclassifier.TextClassifier.EntityConfig.class);
final Bundle extras = in.readBundle();
final String referenceTimeString = in.readString();
final ZonedDateTime referenceTime = referenceTimeString == null
? null : ZonedDateTime.parse(referenceTimeString);
- final SystemTextClassifierMetadata systemTcMetadata = in.readParcelable(null);
+ final SystemTextClassifierMetadata systemTcMetadata = in.readParcelable(null, android.view.textclassifier.SystemTextClassifierMetadata.class);
final Request request = new Request(text, defaultLocales, entityConfig,
/* legacyFallback= */ true, referenceTime, extras);
diff --git a/core/java/android/view/textclassifier/TextSelection.java b/core/java/android/view/textclassifier/TextSelection.java
index c1913f69546c..dda0fcdd44fd 100644
--- a/core/java/android/view/textclassifier/TextSelection.java
+++ b/core/java/android/view/textclassifier/TextSelection.java
@@ -489,9 +489,9 @@ public final class TextSelection implements Parcelable {
final CharSequence text = in.readCharSequence();
final int startIndex = in.readInt();
final int endIndex = in.readInt();
- final LocaleList defaultLocales = in.readParcelable(null);
+ final LocaleList defaultLocales = in.readParcelable(null, android.os.LocaleList.class);
final Bundle extras = in.readBundle();
- final SystemTextClassifierMetadata systemTcMetadata = in.readParcelable(null);
+ final SystemTextClassifierMetadata systemTcMetadata = in.readParcelable(null, android.view.textclassifier.SystemTextClassifierMetadata.class);
final boolean includeTextClassification = in.readBoolean();
final Request request = new Request(text, startIndex, endIndex, defaultLocales,
@@ -548,6 +548,6 @@ public final class TextSelection implements Parcelable {
mEntityConfidence = EntityConfidence.CREATOR.createFromParcel(in);
mId = in.readString();
mExtras = in.readBundle();
- mTextClassification = in.readParcelable(TextClassification.class.getClassLoader());
+ mTextClassification = in.readParcelable(TextClassification.class.getClassLoader(), android.view.textclassifier.TextClassification.class);
}
}
diff --git a/core/java/android/view/translation/TranslationRequest.java b/core/java/android/view/translation/TranslationRequest.java
index 0d41851ca704..027edc21389f 100644
--- a/core/java/android/view/translation/TranslationRequest.java
+++ b/core/java/android/view/translation/TranslationRequest.java
@@ -255,9 +255,9 @@ public final class TranslationRequest implements Parcelable {
int flags = in.readInt();
List<TranslationRequestValue> translationRequestValues = new ArrayList<>();
- in.readParcelableList(translationRequestValues, TranslationRequestValue.class.getClassLoader());
+ in.readParcelableList(translationRequestValues, TranslationRequestValue.class.getClassLoader(), android.view.translation.TranslationRequestValue.class);
List<ViewTranslationRequest> viewTranslationRequests = new ArrayList<>();
- in.readParcelableList(viewTranslationRequests, ViewTranslationRequest.class.getClassLoader());
+ in.readParcelableList(viewTranslationRequests, ViewTranslationRequest.class.getClassLoader(), android.view.translation.ViewTranslationRequest.class);
this.mFlags = flags;
diff --git a/core/java/android/view/translation/TranslationSpec.java b/core/java/android/view/translation/TranslationSpec.java
index efc3d8ba8096..76dda5fbe83b 100644
--- a/core/java/android/view/translation/TranslationSpec.java
+++ b/core/java/android/view/translation/TranslationSpec.java
@@ -64,7 +64,7 @@ public final class TranslationSpec implements Parcelable {
}
static ULocale unparcelLocale(Parcel in) {
- return (ULocale) in.readSerializable();
+ return (ULocale) in.readSerializable(android.icu.util.ULocale.class.getClassLoader(), android.icu.util.ULocale.class);
}
/**
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index ac28c31ecf49..dd70d69a8e02 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -17,6 +17,7 @@
package android.widget;
import static android.view.ContentInfo.SOURCE_DRAG_AND_DROP;
+import static android.widget.TextView.ACCESSIBILITY_ACTION_SMART_START_ID;
import android.R;
import android.animation.ValueAnimator;
@@ -84,6 +85,7 @@ import android.text.style.URLSpan;
import android.util.ArraySet;
import android.util.DisplayMetrics;
import android.util.Log;
+import android.util.Pair;
import android.util.SparseArray;
import android.util.TypedValue;
import android.view.ActionMode;
@@ -112,6 +114,7 @@ import android.view.ViewParent;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import android.view.animation.LinearInterpolator;
import android.view.inputmethod.CorrectionInfo;
import android.view.inputmethod.CursorAnchorInfo;
@@ -449,11 +452,14 @@ public class Editor {
private int mLineChangeSlopMax;
private int mLineChangeSlopMin;
+ private final AccessibilitySmartActions mA11ySmartActions;
+
Editor(TextView textView) {
mTextView = textView;
// Synchronize the filter list, which places the undo input filter at the end.
mTextView.setFilters(mTextView.getFilters());
mProcessTextIntentActionsHandler = new ProcessTextIntentActionsHandler(this);
+ mA11ySmartActions = new AccessibilitySmartActions(mTextView);
mHapticTextHandleEnabled = mTextView.getContext().getResources().getBoolean(
com.android.internal.R.bool.config_enableHapticTextHandle);
@@ -4381,6 +4387,7 @@ public class Editor {
item.setShowAsAction(showAsAction);
mAssistClickHandlers.put(item,
TextClassification.createIntentOnClickListener(action.getActionIntent()));
+ mA11ySmartActions.addAction(action);
return item;
}
@@ -4394,6 +4401,7 @@ public class Editor {
}
i++;
}
+ mA11ySmartActions.reset();
}
private boolean hasLegacyAssistItem(TextClassification classification) {
@@ -7656,7 +7664,7 @@ public class Editor {
private final PackageManager mPackageManager;
private final String mPackageName;
private final SparseArray<Intent> mAccessibilityIntents = new SparseArray<>();
- private final SparseArray<AccessibilityNodeInfo.AccessibilityAction> mAccessibilityActions =
+ private final SparseArray<AccessibilityAction> mAccessibilityActions =
new SparseArray<>();
private final List<ResolveInfo> mSupportedActivities = new ArrayList<>();
@@ -7706,8 +7714,7 @@ public class Editor {
int actionId = TextView.ACCESSIBILITY_ACTION_PROCESS_TEXT_START_ID + i++;
mAccessibilityActions.put(
actionId,
- new AccessibilityNodeInfo.AccessibilityAction(
- actionId, getLabel(resolveInfo)));
+ new AccessibilityAction(actionId, getLabel(resolveInfo)));
mAccessibilityIntents.put(
actionId, createProcessTextIntentForResolveInfo(resolveInfo));
}
@@ -7786,6 +7793,65 @@ public class Editor {
}
}
+ /**
+ * Accessibility helper for "smart" (i.e. textAssist) actions.
+ * Helps ensure that "smart" actions are shown in the accessibility menu.
+ * NOTE that these actions are only available when an action mode is live.
+ *
+ * @hide
+ */
+ private static final class AccessibilitySmartActions {
+
+ private final TextView mTextView;
+ private final SparseArray<Pair<AccessibilityAction, RemoteAction>> mActions =
+ new SparseArray<>();
+
+ private AccessibilitySmartActions(TextView textView) {
+ mTextView = Objects.requireNonNull(textView);
+ }
+
+ private void addAction(RemoteAction action) {
+ final int actionId = ACCESSIBILITY_ACTION_SMART_START_ID + mActions.size();
+ mActions.put(actionId,
+ new Pair(new AccessibilityAction(actionId, action.getTitle()), action));
+ }
+
+ private void reset() {
+ mActions.clear();
+ }
+
+ void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo nodeInfo) {
+ for (int i = 0; i < mActions.size(); i++) {
+ nodeInfo.addAction(mActions.valueAt(i).first);
+ }
+ }
+
+ boolean performAccessibilityAction(int actionId) {
+ final Pair<AccessibilityAction, RemoteAction> pair = mActions.get(actionId);
+ if (pair != null) {
+ TextClassification.createIntentOnClickListener(pair.second.getActionIntent())
+ .onClick(mTextView);
+ return true;
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Initializes the nodeInfo with smart actions.
+ */
+ void onInitializeSmartActionsAccessibilityNodeInfo(AccessibilityNodeInfo nodeInfo) {
+ mA11ySmartActions.onInitializeAccessibilityNodeInfo(nodeInfo);
+ }
+
+ /**
+ * Handles the accessibility action if it is an active smart action.
+ * Return false if this method does not hanle the action.
+ */
+ boolean performSmartActionsAccessibilityAction(int actionId) {
+ return mA11ySmartActions.performAccessibilityAction(actionId);
+ }
+
static void logCursor(String location, @Nullable String msgFormat, Object ... msgArgs) {
if (msgFormat == null) {
Log.d(TAG, location);
diff --git a/core/java/android/widget/ExpandableListView.java b/core/java/android/widget/ExpandableListView.java
index 51869d4e04d5..e243aae81da4 100644
--- a/core/java/android/widget/ExpandableListView.java
+++ b/core/java/android/widget/ExpandableListView.java
@@ -1309,7 +1309,7 @@ public class ExpandableListView extends ListView {
private SavedState(Parcel in) {
super(in);
expandedGroupMetadataList = new ArrayList<ExpandableListConnector.GroupMetadata>();
- in.readList(expandedGroupMetadataList, ExpandableListConnector.class.getClassLoader());
+ in.readList(expandedGroupMetadataList, ExpandableListConnector.class.getClassLoader(), android.widget.ExpandableListConnector.GroupMetadata.class);
}
@Override
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index e60f9a648730..b21d08c8e664 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -1489,7 +1489,7 @@ public class RemoteViews implements Parcelable, Filter {
SetRippleDrawableColor(Parcel parcel) {
viewId = parcel.readInt();
- mColorStateList = parcel.readParcelable(null);
+ mColorStateList = parcel.readParcelable(null, android.content.res.ColorStateList.class);
}
public void writeToParcel(Parcel dest, int flags) {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 0fe06befa789..41c540116928 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -350,6 +350,7 @@ import java.util.function.Supplier;
* @attr ref android.R.styleable#TextView_breakStrategy
* @attr ref android.R.styleable#TextView_hyphenationFrequency
* @attr ref android.R.styleable#TextView_lineBreakStyle
+ * @attr ref android.R.styleable#TextView_lineBreakWordStyle
* @attr ref android.R.styleable#TextView_autoSizeTextType
* @attr ref android.R.styleable#TextView_autoSizeMinTextSize
* @attr ref android.R.styleable#TextView_autoSizeMaxTextSize
@@ -442,6 +443,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
// Accessibility action start id for "process text" actions.
static final int ACCESSIBILITY_ACTION_PROCESS_TEXT_START_ID = 0x10000100;
+ /** Accessibility action start id for "smart" actions. @hide */
+ static final int ACCESSIBILITY_ACTION_SMART_START_ID = 0x10001000;
+
/**
* @hide
*/
@@ -458,6 +462,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
private static final int FLOATING_TOOLBAR_SELECT_ALL_REFRESH_DELAY = 500;
+ // The default value of the line break style.
+ private static final int DEFAULT_LINE_BREAK_STYLE = LineBreakConfig.LINE_BREAK_STYLE_NONE;
+
+ // The default value of the line break word style.
+ private static final int DEFAULT_LINE_BREAK_WORD_STYLE =
+ LineBreakConfig.LINE_BREAK_WORD_STYLE_NONE;
+
/**
* This change ID enables the fallback text line spacing (line height) for BoringLayout.
* @hide
@@ -1450,6 +1461,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
a.getInt(attr, LineBreakConfig.LINE_BREAK_STYLE_NONE));
break;
+ case com.android.internal.R.styleable.TextView_lineBreakWordStyle:
+ mLineBreakConfig.setLineBreakWordStyle(
+ a.getInt(attr, LineBreakConfig.LINE_BREAK_WORD_STYLE_NONE));
+ break;
+
case com.android.internal.R.styleable.TextView_autoSizeTextType:
mAutoSizeTextType = a.getInt(attr, AUTO_SIZE_TEXT_TYPE_NONE);
break;
@@ -3982,6 +3998,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
float mLetterSpacing = 0;
String mFontFeatureSettings = null;
String mFontVariationSettings = null;
+ boolean mHasLineBreakStyle = false;
+ boolean mHasLineBreakWordStyle = false;
+ int mLineBreakStyle = DEFAULT_LINE_BREAK_STYLE;
+ int mLineBreakWordStyle = DEFAULT_LINE_BREAK_WORD_STYLE;
@Override
public String toString() {
@@ -4012,6 +4032,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
+ " mLetterSpacing:" + mLetterSpacing + "\n"
+ " mFontFeatureSettings:" + mFontFeatureSettings + "\n"
+ " mFontVariationSettings:" + mFontVariationSettings + "\n"
+ + " mHasLineBreakStyle:" + mHasLineBreakStyle + "\n"
+ + " mHasLineBreakWordStyle:" + mHasLineBreakWordStyle + "\n"
+ + " mLineBreakStyle:" + mLineBreakStyle + "\n"
+ + " mLineBreakWordStyle:" + mLineBreakWordStyle + "\n"
+ "}";
}
}
@@ -4059,6 +4083,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
com.android.internal.R.styleable.TextAppearance_fontFeatureSettings);
sAppearanceValues.put(com.android.internal.R.styleable.TextView_fontVariationSettings,
com.android.internal.R.styleable.TextAppearance_fontVariationSettings);
+ sAppearanceValues.put(com.android.internal.R.styleable.TextView_lineBreakStyle,
+ com.android.internal.R.styleable.TextAppearance_lineBreakStyle);
+ sAppearanceValues.put(com.android.internal.R.styleable.TextView_lineBreakWordStyle,
+ com.android.internal.R.styleable.TextAppearance_lineBreakWordStyle);
}
/**
@@ -4174,6 +4202,16 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
case com.android.internal.R.styleable.TextAppearance_fontVariationSettings:
attributes.mFontVariationSettings = appearance.getString(attr);
break;
+ case com.android.internal.R.styleable.TextAppearance_lineBreakStyle:
+ attributes.mHasLineBreakStyle = true;
+ attributes.mLineBreakStyle =
+ appearance.getInt(attr, attributes.mLineBreakStyle);
+ break;
+ case com.android.internal.R.styleable.TextAppearance_lineBreakWordStyle:
+ attributes.mHasLineBreakWordStyle = true;
+ attributes.mLineBreakWordStyle =
+ appearance.getInt(attr, attributes.mLineBreakWordStyle);
+ break;
default:
}
}
@@ -4239,9 +4277,46 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
if (attributes.mFontVariationSettings != null) {
setFontVariationSettings(attributes.mFontVariationSettings);
}
+
+ if (attributes.mHasLineBreakStyle || attributes.mHasLineBreakWordStyle) {
+ updateLineBreakConfigFromTextAppearance(attributes.mHasLineBreakStyle,
+ attributes.mHasLineBreakWordStyle, attributes.mLineBreakStyle,
+ attributes.mLineBreakWordStyle);
+ }
}
/**
+ * Updates the LineBreakConfig from the TextAppearance.
+ *
+ * This method updates the given line configuration from the TextAppearance. This method will
+ * request new layout if line break config has been changed.
+ *
+ * @param isLineBreakStyleSpecified true if the line break style is specified.
+ * @param isLineBreakWordStyleSpecified true if the line break word style is specified.
+ * @param lineBreakStyle the value of the line break style in the TextAppearance.
+ * @param lineBreakWordStyle the value of the line break word style in the TextAppearance.
+ */
+ private void updateLineBreakConfigFromTextAppearance(boolean isLineBreakStyleSpecified,
+ boolean isLineBreakWordStyleSpecified,
+ @LineBreakConfig.LineBreakStyle int lineBreakStyle,
+ @LineBreakConfig.LineBreakWordStyle int lineBreakWordStyle) {
+ boolean updated = false;
+ if (isLineBreakStyleSpecified && mLineBreakConfig.getLineBreakStyle() != lineBreakStyle) {
+ mLineBreakConfig.setLineBreakStyle(lineBreakStyle);
+ updated = true;
+ }
+ if (isLineBreakWordStyleSpecified
+ && mLineBreakConfig.getLineBreakWordStyle() != lineBreakWordStyle) {
+ mLineBreakConfig.setLineBreakWordStyle(lineBreakWordStyle);
+ updated = true;
+ }
+ if (updated && mLayout != null) {
+ nullLayouts();
+ requestLayout();
+ invalidate();
+ }
+ }
+ /**
* Get the default primary {@link Locale} of the text in this TextView. This will always be
* the first member of {@link #getTextLocales()}.
* @return the default primary {@link Locale} of the text in this TextView.
@@ -4797,18 +4872,29 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
/**
* Sets line break configuration indicates which strategy needs to be used when calculating the
- * text wrapping. There are thee strategies for the line break style(lb):
+ * text wrapping.
+ * <P>
+ * There are two types of line break rules that can be configured at the same time. One is
+ * line break style(lb) and the other is line break word style(lw). The line break style
+ * affects rule-based breaking. The line break word style affects dictionary-based breaking
+ * and provide phrase-based breaking opportunities. There are several types for the
+ * line break style:
* {@link LineBreakConfig#LINE_BREAK_STYLE_LOOSE},
* {@link LineBreakConfig#LINE_BREAK_STYLE_NORMAL} and
* {@link LineBreakConfig#LINE_BREAK_STYLE_STRICT}.
- * The default value of the line break style is {@link LineBreakConfig#LINE_BREAK_STYLE_NONE},
- * which means no line break style is specified.
+ * The type for the line break word style is
+ * {@link LineBreakConfig#LINE_BREAK_WORD_STYLE_PHRASE}.
+ * The default values of the line break style and the line break word style are
+ * {@link LineBreakConfig#LINE_BREAK_STYLE_NONE} and
+ * {@link LineBreakConfig#LINE_BREAK_WORD_STYLE_NONE} respectively, indicating that no line
+ * breaking rules are specified.
* See <a href="https://drafts.csswg.org/css-text/#line-break-property">
* the line-break property</a>
*
* @param lineBreakConfig the line break config for text wrapping.
*/
public void setLineBreakConfig(@NonNull LineBreakConfig lineBreakConfig) {
+ Objects.requireNonNull(lineBreakConfig);
if (mLineBreakConfig.equals(lineBreakConfig)) {
return;
}
@@ -4855,7 +4941,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
mTextDir = params.getTextDirection();
mBreakStrategy = params.getBreakStrategy();
mHyphenationFrequency = params.getHyphenationFrequency();
- mLineBreakConfig.set(params.getLineBreakConfig());
+ if (params.getLineBreakConfig() != null) {
+ mLineBreakConfig.set(params.getLineBreakConfig());
+ } else {
+ // Set default value if the line break config in the PrecomputedText.Params is null.
+ mLineBreakConfig.setLineBreakStyle(DEFAULT_LINE_BREAK_STYLE);
+ mLineBreakConfig.setLineBreakWordStyle(DEFAULT_LINE_BREAK_WORD_STYLE);
+ }
if (mLayout != null) {
nullLayouts();
requestLayout();
@@ -12223,6 +12315,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
if (canProcessText()) { // also implies mEditor is not null.
mEditor.mProcessTextIntentActionsHandler.onInitializeAccessibilityNodeInfo(info);
+ mEditor.onInitializeSmartActionsAccessibilityNodeInfo(info);
}
}
@@ -12426,9 +12519,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
*/
@Override
public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
- if (mEditor != null
- && mEditor.mProcessTextIntentActionsHandler.performAccessibilityAction(action)) {
- return true;
+ if (mEditor != null) {
+ if (mEditor.mProcessTextIntentActionsHandler.performAccessibilityAction(action)
+ || mEditor.performSmartActionsAccessibilityAction(action)) {
+ return true;
+ }
}
switch (action) {
case AccessibilityNodeInfo.ACTION_CLICK: {
diff --git a/core/java/android/window/BackNavigationInfo.aidl b/core/java/android/window/BackNavigationInfo.aidl
new file mode 100644
index 000000000000..1529902b9c20
--- /dev/null
+++ b/core/java/android/window/BackNavigationInfo.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.window;
+
+/**
+ * @hide
+ */
+parcelable BackNavigationInfo; \ No newline at end of file
diff --git a/core/java/android/window/BackNavigationInfo.java b/core/java/android/window/BackNavigationInfo.java
new file mode 100644
index 000000000000..571714cc05d5
--- /dev/null
+++ b/core/java/android/window/BackNavigationInfo.java
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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 java.util.Objects.requireNonNull;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.WindowConfiguration;
+import android.hardware.HardwareBuffer;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.RemoteCallback;
+import android.view.SurfaceControl;
+
+/**
+ * Information to be sent to SysUI about a back event.
+ *
+ * @hide
+ */
+public final class BackNavigationInfo implements Parcelable {
+
+ /**
+ * The target of the back navigation is undefined.
+ */
+ public static final int TYPE_UNDEFINED = -1;
+
+ /**
+ * Navigating back will close the currently visible dialog
+ */
+ public static final int TYPE_DIALOG_CLOSE = 0;
+
+ /**
+ * Navigating back will bring the user back to the home screen
+ */
+ public static final int TYPE_RETURN_TO_HOME = 1;
+
+ /**
+ * Navigating back will bring the user to the previous activity in the same Task
+ */
+ public static final int TYPE_CROSS_ACTIVITY = 2;
+
+ /**
+ * Navigating back will bring the user to the previous activity in the previous Task
+ */
+ public static final int TYPE_CROSS_TASK = 3;
+
+ /**
+ * Defines the type of back destinations a back even can lead to. This is used to define the
+ * type of animation that need to be run on SystemUI.
+ */
+ @IntDef(prefix = "TYPE_", value = {
+ TYPE_UNDEFINED,
+ TYPE_DIALOG_CLOSE,
+ TYPE_RETURN_TO_HOME,
+ TYPE_CROSS_ACTIVITY,
+ TYPE_CROSS_TASK})
+ @interface BackTargetType {
+ }
+
+ private final int mType;
+ @Nullable
+ private final SurfaceControl mDepartingWindowContainer;
+ @Nullable
+ private final SurfaceControl mScreenshotSurface;
+ @Nullable
+ private final HardwareBuffer mScreenshotBuffer;
+ @Nullable
+ private final RemoteCallback mRemoteCallback;
+ @Nullable
+ private final WindowConfiguration mTaskWindowConfiguration;
+
+ /**
+ * Create a new {@link BackNavigationInfo} instance.
+ *
+ * @param type The {@link BackTargetType} of the destination (what will be displayed after
+ * the back action)
+ * @param topWindowLeash The leash to animate away the current topWindow. The consumer
+ * of the leash is responsible for removing it.
+ * @param screenshotSurface The screenshot of the previous activity to be displayed.
+ * @param screenshotBuffer A buffer containing a screenshot used to display the activity.
+ * See {@link #getScreenshotHardwareBuffer()} for information
+ * about nullity.
+ * @param taskWindowConfiguration The window configuration of the Task being animated
+ * beneath.
+ * @param onBackNavigationDone The callback to be called once the client is done with the back
+ * preview.
+ */
+ public BackNavigationInfo(@BackTargetType int type,
+ @Nullable SurfaceControl topWindowLeash,
+ @Nullable SurfaceControl screenshotSurface,
+ @Nullable HardwareBuffer screenshotBuffer,
+ @Nullable WindowConfiguration taskWindowConfiguration,
+ @NonNull RemoteCallback onBackNavigationDone) {
+ mType = type;
+ mDepartingWindowContainer = topWindowLeash;
+ mScreenshotSurface = screenshotSurface;
+ mScreenshotBuffer = screenshotBuffer;
+ mTaskWindowConfiguration = taskWindowConfiguration;
+ mRemoteCallback = onBackNavigationDone;
+ }
+
+ private BackNavigationInfo(@NonNull Parcel in) {
+ mType = in.readInt();
+ mDepartingWindowContainer = in.readTypedObject(SurfaceControl.CREATOR);
+ mScreenshotSurface = in.readTypedObject(SurfaceControl.CREATOR);
+ mScreenshotBuffer = in.readTypedObject(HardwareBuffer.CREATOR);
+ mTaskWindowConfiguration = in.readTypedObject(WindowConfiguration.CREATOR);
+ mRemoteCallback = requireNonNull(in.readTypedObject(RemoteCallback.CREATOR));
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mType);
+ dest.writeTypedObject(mDepartingWindowContainer, flags);
+ dest.writeTypedObject(mScreenshotSurface, flags);
+ dest.writeTypedObject(mScreenshotBuffer, flags);
+ dest.writeTypedObject(mTaskWindowConfiguration, flags);
+ dest.writeTypedObject(mRemoteCallback, flags);
+ }
+
+ /**
+ * Returns the type of back navigation that is about to happen.
+ * @see BackTargetType
+ */
+ public @BackTargetType int getType() {
+ return mType;
+ }
+
+ /**
+ * Returns a leash to the top window container that needs to be animated. This can be null if
+ * the back animation is controlled by the application.
+ */
+ @Nullable
+ public SurfaceControl getDepartingWindowContainer() {
+ return mDepartingWindowContainer;
+ }
+
+ /**
+ * Returns the {@link SurfaceControl} that should be used to display a screenshot of the
+ * previous activity.
+ */
+ @Nullable
+ public SurfaceControl getScreenshotSurface() {
+ return mScreenshotSurface;
+ }
+
+ /**
+ * Returns the {@link HardwareBuffer} containing the screenshot the activity about to be
+ * shown. This can be null if one of the following conditions is met:
+ * <ul>
+ * <li>The screenshot is not available
+ * <li> The previous activity is the home screen ( {@link #TYPE_RETURN_TO_HOME}
+ * <li> The current window is a dialog ({@link #TYPE_DIALOG_CLOSE}
+ * <li> The back animation is controlled by the application
+ * </ul>
+ */
+ @Nullable
+ public HardwareBuffer getScreenshotHardwareBuffer() {
+ return mScreenshotBuffer;
+ }
+
+ /**
+ * Returns the {@link WindowConfiguration} of the current task. This is null when the top
+ * application is controlling the back animation.
+ */
+ @Nullable
+ public WindowConfiguration getTaskWindowConfiguration() {
+ return mTaskWindowConfiguration;
+ }
+
+ /**
+ * Callback to be called when the back preview is finished in order to notify the server that
+ * it can clean up the resources created for the animation.
+ */
+ public void onBackNavigationFinished() {
+ mRemoteCallback.sendResult(null);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Creator<BackNavigationInfo> CREATOR = new Creator<BackNavigationInfo>() {
+ @Override
+ public BackNavigationInfo createFromParcel(Parcel in) {
+ return new BackNavigationInfo(in);
+ }
+
+ @Override
+ public BackNavigationInfo[] newArray(int size) {
+ return new BackNavigationInfo[size];
+ }
+ };
+
+ @Override
+ public String toString() {
+ return "BackNavigationInfo{"
+ + "mType=" + typeToString(mType) + " (" + mType + ")"
+ + ", mDepartingWindowContainer=" + mDepartingWindowContainer
+ + ", mScreenshotSurface=" + mScreenshotSurface
+ + ", mTaskWindowConfiguration= " + mTaskWindowConfiguration
+ + ", mScreenshotBuffer=" + mScreenshotBuffer
+ + ", mRemoteCallback=" + mRemoteCallback
+ + '}';
+ }
+
+ /**
+ * Translates the {@link BackNavigationInfo} integer type to its String representation
+ */
+ public static String typeToString(@BackTargetType int type) {
+ switch (type) {
+ case TYPE_UNDEFINED:
+ return "TYPE_UNDEFINED";
+ case TYPE_DIALOG_CLOSE:
+ return "TYPE_DIALOG_CLOSE";
+ case TYPE_RETURN_TO_HOME:
+ return "TYPE_RETURN_TO_HOME";
+ case TYPE_CROSS_ACTIVITY:
+ return "TYPE_CROSS_ACTIVITY";
+ case TYPE_CROSS_TASK:
+ return "TYPE_CROSS_TASK";
+ }
+ return String.valueOf(type);
+ }
+}
diff --git a/core/java/android/window/IOnBackInvokedCallback.aidl b/core/java/android/window/IOnBackInvokedCallback.aidl
new file mode 100644
index 000000000000..a42863c3126f
--- /dev/null
+++ b/core/java/android/window/IOnBackInvokedCallback.aidl
@@ -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
+
+ */
+
+package android.window;
+
+/**
+ * Interface that wraps a {@link OnBackInvokedCallback} object, to be stored in window manager
+ * and called from back handling process when back is invoked.
+ *
+ * @hide
+ */
+oneway interface IOnBackInvokedCallback {
+ /**
+ * Called when a back gesture has been started, or back button has been pressed down.
+ * Wraps {@link OnBackInvokedCallback#onBackStarted()}.
+ */
+ void onBackStarted();
+
+ /**
+ * Called on back gesture progress.
+ * Wraps {@link OnBackInvokedCallback#onBackProgressed()}.
+ *
+ * @param touchX Absolute X location of the touch point.
+ * @param touchY Absolute Y location of the touch point.
+ * @param progress Value between 0 and 1 on how far along the back gesture is.
+ */
+ void onBackProgressed(int touchX, int touchY, float progress);
+
+ /**
+ * Called when a back gesture or back button press has been cancelled.
+ * Wraps {@link OnBackInvokedCallback#onBackCancelled()}.
+ */
+ void onBackCancelled();
+
+ /**
+ * Called when a back gesture has been completed and committed, or back button pressed
+ * has been released and committed.
+ * Wraps {@link OnBackInvokedCallback#onBackInvoked()}.
+ */
+ void onBackInvoked();
+}
diff --git a/core/java/android/window/IOnFpsCallbackListener.aidl b/core/java/android/window/IOnFpsCallbackListener.aidl
new file mode 100644
index 000000000000..3091df3b23a3
--- /dev/null
+++ b/core/java/android/window/IOnFpsCallbackListener.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.window;
+
+/**
+ * @hide
+ */
+oneway interface IOnFpsCallbackListener {
+
+ /**
+ * Reports the fps from the registered task
+ * @param fps The frame rate per second of the task that has the registered task id
+ * and its children.
+ */
+ void onFpsReported(in float fps);
+}
diff --git a/core/java/android/window/TaskFpsCallback.java b/core/java/android/window/TaskFpsCallback.java
new file mode 100644
index 000000000000..a8e01b6df4b8
--- /dev/null
+++ b/core/java/android/window/TaskFpsCallback.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.BinderThread;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.RemoteException;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Callback for sampling the frames per second for a task and its children.
+ * This should only be used by a system component that needs to listen to a task's
+ * tree's FPS when it is not actively submitting transactions for that corresponding SurfaceControl.
+ * Otherwise, ASurfaceTransaction_OnComplete callbacks should be used.
+ *
+ * Each callback can only register for receiving FPS report for one task id until
+ * {@link WindowManager#unregister()} is called.
+ *
+ * @hide
+ */
+@SystemApi
+public final class TaskFpsCallback {
+
+ /**
+ * Listener interface to receive frame per second of a task.
+ */
+ public interface OnFpsCallbackListener {
+ /**
+ * Reports the fps from the registered task
+ * @param fps The frame per second of the task that has the registered task id
+ * and its children.
+ */
+ void onFpsReported(float fps);
+ }
+
+ private final IOnFpsCallbackListener mListener;
+
+ public TaskFpsCallback(@NonNull Executor executor, @NonNull OnFpsCallbackListener listener) {
+ mListener = new IOnFpsCallbackListener.Stub() {
+ @Override
+ public void onFpsReported(float fps) {
+ executor.execute(() -> {
+ listener.onFpsReported(fps);
+ });
+ }
+ };
+ }
+
+ /**
+ * @hide
+ */
+ public IOnFpsCallbackListener getListener() {
+ return mListener;
+ }
+
+ /**
+ * Dispatch the collected sample.
+ *
+ * Called from native code on a binder thread.
+ */
+ @BinderThread
+ private static void dispatchOnFpsReported(
+ @NonNull IOnFpsCallbackListener listener, float fps) {
+ try {
+ listener.onFpsReported(fps);
+ } catch (RemoteException e) {
+ /* ignore */
+ }
+ }
+}
diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java
new file mode 100644
index 000000000000..29786046e49c
--- /dev/null
+++ b/core/java/android/window/WindowOnBackInvokedDispatcher.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 android.window;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.IWindow;
+import android.view.IWindowSession;
+import android.view.OnBackInvokedCallback;
+import android.view.OnBackInvokedDispatcher;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.TreeMap;
+
+/**
+ * Provides window based implementation of {@link OnBackInvokedDispatcher}.
+ *
+ * Callbacks with higher priorities receive back dispatching first.
+ * Within the same priority, callbacks receive back dispatching in the reverse order
+ * in which they are added.
+ *
+ * When the top priority callback is updated, the new callback is propagated to the Window Manager
+ * if the window the instance is associated with has been attached. It is allowed to register /
+ * unregister {@link OnBackInvokedCallback}s before the window is attached, although callbacks
+ * will not receive dispatches until window attachment.
+ *
+ * @hide
+ */
+public class WindowOnBackInvokedDispatcher extends OnBackInvokedDispatcher {
+ private IWindowSession mWindowSession;
+ private IWindow mWindow;
+ private static final String TAG = "WindowOnBackDispatcher";
+ private static final boolean DEBUG = false;
+
+ /** The currently most prioritized callback. */
+ @Nullable
+ private OnBackInvokedCallbackWrapper mTopCallback;
+
+ /** Convenience hashmap to quickly decide if a callback has been added. */
+ private final HashMap<OnBackInvokedCallback, Integer> mAllCallbacks = new HashMap<>();
+ /** Holds all callbacks by priorities. */
+ private final TreeMap<Integer, ArrayList<OnBackInvokedCallback>>
+ mOnBackInvokedCallbacks = new TreeMap<>();
+
+ /**
+ * Sends the pending top callback (if one exists) to WM when the view root
+ * is attached a window.
+ */
+ public void attachToWindow(@NonNull IWindowSession windowSession, @NonNull IWindow window) {
+ mWindowSession = windowSession;
+ mWindow = window;
+ if (mTopCallback != null) {
+ setTopOnBackInvokedCallback(mTopCallback);
+ }
+ }
+
+ /** Detaches the dispatcher instance from its window. */
+ public void detachFromWindow() {
+ mWindow = null;
+ mWindowSession = null;
+ }
+
+ // TODO: Take an Executor for the callback to run on.
+ @Override
+ public void registerOnBackInvokedCallback(
+ @NonNull OnBackInvokedCallback callback, @Priority int priority) {
+ if (!mOnBackInvokedCallbacks.containsKey(priority)) {
+ mOnBackInvokedCallbacks.put(priority, new ArrayList<>());
+ }
+ ArrayList<OnBackInvokedCallback> callbacks = mOnBackInvokedCallbacks.get(priority);
+
+ // If callback has already been added, remove it and re-add it.
+ if (mAllCallbacks.containsKey(callback)) {
+ if (DEBUG) {
+ Log.i(TAG, "Callback already added. Removing and re-adding it.");
+ }
+ Integer prevPriority = mAllCallbacks.get(callback);
+ mOnBackInvokedCallbacks.get(prevPriority).remove(callback);
+ }
+
+ callbacks.add(callback);
+ mAllCallbacks.put(callback, priority);
+ if (mTopCallback == null || (mTopCallback.getCallback() != callback
+ && mAllCallbacks.get(mTopCallback.getCallback()) <= priority)) {
+ setTopOnBackInvokedCallback(new OnBackInvokedCallbackWrapper(callback, priority));
+ }
+ }
+
+ @Override
+ public void unregisterOnBackInvokedCallback(@NonNull OnBackInvokedCallback callback) {
+ if (!mAllCallbacks.containsKey(callback)) {
+ if (DEBUG) {
+ Log.i(TAG, "Callback not found. returning...");
+ }
+ return;
+ }
+ Integer priority = mAllCallbacks.get(callback);
+ mOnBackInvokedCallbacks.get(priority).remove(callback);
+ mAllCallbacks.remove(callback);
+ if (mTopCallback != null && mTopCallback.getCallback() == callback) {
+ findAndSetTopOnBackInvokedCallback();
+ }
+ }
+
+ /** Clears all registered callbacks on the instance. */
+ public void clear() {
+ mAllCallbacks.clear();
+ mTopCallback = null;
+ mOnBackInvokedCallbacks.clear();
+ }
+
+ /**
+ * Iterates through all callbacks to find the most prioritized one and pushes it to
+ * window manager.
+ */
+ private void findAndSetTopOnBackInvokedCallback() {
+ if (mAllCallbacks.isEmpty()) {
+ setTopOnBackInvokedCallback(null);
+ return;
+ }
+
+ for (Integer priority : mOnBackInvokedCallbacks.descendingKeySet()) {
+ ArrayList<OnBackInvokedCallback> callbacks = mOnBackInvokedCallbacks.get(priority);
+ if (!callbacks.isEmpty()) {
+ OnBackInvokedCallbackWrapper callback = new OnBackInvokedCallbackWrapper(
+ callbacks.get(callbacks.size() - 1), priority);
+ setTopOnBackInvokedCallback(callback);
+ return;
+ }
+ }
+ setTopOnBackInvokedCallback(null);
+ }
+
+ // Pushes the top priority callback to window manager.
+ private void setTopOnBackInvokedCallback(@Nullable OnBackInvokedCallbackWrapper callback) {
+ mTopCallback = callback;
+ if (mWindowSession == null || mWindow == null) {
+ return;
+ }
+ try {
+ mWindowSession.setOnBackInvokedCallback(mWindow, mTopCallback);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to set OnBackInvokedCallback to WM. Error: " + e);
+ }
+ }
+
+ private class OnBackInvokedCallbackWrapper extends IOnBackInvokedCallback.Stub {
+ private final OnBackInvokedCallback mCallback;
+ private final @Priority int mPriority;
+
+ OnBackInvokedCallbackWrapper(
+ @NonNull OnBackInvokedCallback callback, @Priority int priority) {
+ mCallback = callback;
+ mPriority = priority;
+ }
+
+ @NonNull
+ public OnBackInvokedCallback getCallback() {
+ return mCallback;
+ }
+
+ @Override
+ public void onBackStarted() throws RemoteException {
+ Handler.getMain().post(() -> mCallback.onBackStarted());
+ }
+
+ @Override
+ public void onBackProgressed(int touchX, int touchY, float progress)
+ throws RemoteException {
+ Handler.getMain().post(() -> mCallback.onBackProgressed(touchX, touchY, progress));
+ }
+
+ @Override
+ public void onBackCancelled() throws RemoteException {
+ Handler.getMain().post(() -> mCallback.onBackCancelled());
+ }
+
+ @Override
+ public void onBackInvoked() throws RemoteException {
+ Handler.getMain().post(() -> mCallback.onBackInvoked());
+ }
+ }
+}
diff --git a/core/java/com/android/ims/internal/uce/options/OptionsCapInfo.java b/core/java/com/android/ims/internal/uce/options/OptionsCapInfo.java
index 6f83bf3224a8..d709acfc2872 100644
--- a/core/java/com/android/ims/internal/uce/options/OptionsCapInfo.java
+++ b/core/java/com/android/ims/internal/uce/options/OptionsCapInfo.java
@@ -89,6 +89,6 @@ public class OptionsCapInfo implements Parcelable {
public void readFromParcel(Parcel source) {
mSdp = source.readString();
- mCapInfo = source.readParcelable(CapInfo.class.getClassLoader());
+ mCapInfo = source.readParcelable(CapInfo.class.getClassLoader(), com.android.ims.internal.uce.common.CapInfo.class);
}
} \ No newline at end of file
diff --git a/core/java/com/android/ims/internal/uce/options/OptionsCmdStatus.java b/core/java/com/android/ims/internal/uce/options/OptionsCmdStatus.java
index 461f8bfb48c8..559d61b20d8c 100644
--- a/core/java/com/android/ims/internal/uce/options/OptionsCmdStatus.java
+++ b/core/java/com/android/ims/internal/uce/options/OptionsCmdStatus.java
@@ -147,8 +147,8 @@ public class OptionsCmdStatus implements Parcelable {
/** @hide */
public void readFromParcel(Parcel source) {
mUserData = source.readInt();
- mCmdId = source.readParcelable(OptionsCmdId.class.getClassLoader());
- mStatus = source.readParcelable(StatusCode.class.getClassLoader());
- mCapInfo = source.readParcelable(CapInfo.class.getClassLoader());
+ mCmdId = source.readParcelable(OptionsCmdId.class.getClassLoader(), com.android.ims.internal.uce.options.OptionsCmdId.class);
+ mStatus = source.readParcelable(StatusCode.class.getClassLoader(), com.android.ims.internal.uce.common.StatusCode.class);
+ mCapInfo = source.readParcelable(CapInfo.class.getClassLoader(), com.android.ims.internal.uce.common.CapInfo.class);
}
} \ No newline at end of file
diff --git a/core/java/com/android/ims/internal/uce/options/OptionsSipResponse.java b/core/java/com/android/ims/internal/uce/options/OptionsSipResponse.java
index 32420816f5ab..160f9ebaebc8 100644
--- a/core/java/com/android/ims/internal/uce/options/OptionsSipResponse.java
+++ b/core/java/com/android/ims/internal/uce/options/OptionsSipResponse.java
@@ -180,7 +180,7 @@ public class OptionsSipResponse implements Parcelable {
mRequestId = source.readInt();
mSipResponseCode = source.readInt();
mReasonPhrase = source.readString();
- mCmdId = source.readParcelable(OptionsCmdId.class.getClassLoader());
+ mCmdId = source.readParcelable(OptionsCmdId.class.getClassLoader(), com.android.ims.internal.uce.options.OptionsCmdId.class);
mRetryAfter = source.readInt();
mReasonHeader = source.readString();
}
diff --git a/core/java/com/android/ims/internal/uce/presence/PresCapInfo.java b/core/java/com/android/ims/internal/uce/presence/PresCapInfo.java
index ec8b6bfa4ef3..f0ee5f3bb77d 100644
--- a/core/java/com/android/ims/internal/uce/presence/PresCapInfo.java
+++ b/core/java/com/android/ims/internal/uce/presence/PresCapInfo.java
@@ -105,6 +105,6 @@ public class PresCapInfo implements Parcelable {
/** @hide */
public void readFromParcel(Parcel source) {
mContactUri = source.readString();
- mCapInfo = source.readParcelable(CapInfo.class.getClassLoader());
+ mCapInfo = source.readParcelable(CapInfo.class.getClassLoader(), com.android.ims.internal.uce.common.CapInfo.class);
}
}
diff --git a/core/java/com/android/ims/internal/uce/presence/PresCmdStatus.java b/core/java/com/android/ims/internal/uce/presence/PresCmdStatus.java
index 7e22106f3be3..8fbb000c20f5 100644
--- a/core/java/com/android/ims/internal/uce/presence/PresCmdStatus.java
+++ b/core/java/com/android/ims/internal/uce/presence/PresCmdStatus.java
@@ -146,8 +146,8 @@ public class PresCmdStatus implements Parcelable{
public void readFromParcel(Parcel source) {
mUserData = source.readInt();
mRequestId = source.readInt();
- mCmdId = source.readParcelable(PresCmdId.class.getClassLoader());
- mStatus = source.readParcelable(StatusCode.class.getClassLoader());
+ mCmdId = source.readParcelable(PresCmdId.class.getClassLoader(), com.android.ims.internal.uce.presence.PresCmdId.class);
+ mStatus = source.readParcelable(StatusCode.class.getClassLoader(), com.android.ims.internal.uce.common.StatusCode.class);
}
} \ No newline at end of file
diff --git a/core/java/com/android/ims/internal/uce/presence/PresResInfo.java b/core/java/com/android/ims/internal/uce/presence/PresResInfo.java
index 2f797b41b14f..954c2b61c286 100644
--- a/core/java/com/android/ims/internal/uce/presence/PresResInfo.java
+++ b/core/java/com/android/ims/internal/uce/presence/PresResInfo.java
@@ -122,6 +122,6 @@ public class PresResInfo implements Parcelable {
public void readFromParcel(Parcel source) {
mResUri = source.readString();
mDisplayName = source.readString();
- mInstanceInfo = source.readParcelable(PresResInstanceInfo.class.getClassLoader());
+ mInstanceInfo = source.readParcelable(PresResInstanceInfo.class.getClassLoader(), com.android.ims.internal.uce.presence.PresResInstanceInfo.class);
}
} \ No newline at end of file
diff --git a/core/java/com/android/ims/internal/uce/presence/PresRlmiInfo.java b/core/java/com/android/ims/internal/uce/presence/PresRlmiInfo.java
index e33aa1303886..63247dbd8172 100644
--- a/core/java/com/android/ims/internal/uce/presence/PresRlmiInfo.java
+++ b/core/java/com/android/ims/internal/uce/presence/PresRlmiInfo.java
@@ -236,7 +236,7 @@ public class PresRlmiInfo implements Parcelable {
mListName = source.readString();
mRequestId = source.readInt();
mPresSubscriptionState = source.readParcelable(
- PresSubscriptionState.class.getClassLoader());
+ PresSubscriptionState.class.getClassLoader(), com.android.ims.internal.uce.presence.PresSubscriptionState.class);
mSubscriptionExpireTime = source.readInt();
mSubscriptionTerminatedReason = source.readString();
}
diff --git a/core/java/com/android/ims/internal/uce/presence/PresSipResponse.java b/core/java/com/android/ims/internal/uce/presence/PresSipResponse.java
index 5e394efed294..8097a3797556 100644
--- a/core/java/com/android/ims/internal/uce/presence/PresSipResponse.java
+++ b/core/java/com/android/ims/internal/uce/presence/PresSipResponse.java
@@ -185,7 +185,7 @@ public class PresSipResponse implements Parcelable {
mRequestId = source.readInt();
mSipResponseCode = source.readInt();
mReasonPhrase = source.readString();
- mCmdId = source.readParcelable(PresCmdId.class.getClassLoader());
+ mCmdId = source.readParcelable(PresCmdId.class.getClassLoader(), com.android.ims.internal.uce.presence.PresCmdId.class);
mRetryAfter = source.readInt();
mReasonHeader = source.readString();
}
diff --git a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
index 14fd4c20e648..40ca9fb22d09 100644
--- a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
+++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
@@ -90,6 +90,14 @@ public class AccessibilityShortcutController {
public static final ComponentName ACCESSIBILITY_BUTTON_COMPONENT_NAME =
new ComponentName("com.android.server.accessibility", "AccessibilityButton");
+ public static final ComponentName COLOR_INVERSION_TILE_COMPONENT_NAME =
+ new ComponentName("com.android.server.accessibility", "ColorInversionTile");
+ public static final ComponentName DALTONIZER_TILE_COMPONENT_NAME =
+ new ComponentName("com.android.server.accessibility", "ColorCorrectionTile");
+ public static final ComponentName ONE_HANDED_TILE_COMPONENT_NAME =
+ new ComponentName("com.android.server.accessibility", "OneHandedModeTile");
+ public static final ComponentName REDUCE_BRIGHT_COLORS_TILE_SERVICE_COMPONENT_NAME =
+ new ComponentName("com.android.server.accessibility", "ReduceBrightColorsTile");
private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
diff --git a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
index b723db287823..4ad232ad25eb 100644
--- a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
@@ -467,8 +467,21 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter {
}
protected void showEmptyState(ResolverListAdapter activeListAdapter,
+ @DrawableRes int iconRes, String title, String subtitle) {
+ showEmptyState(activeListAdapter, iconRes, title, subtitle, /* buttonOnClick */ null);
+ }
+
+ protected void showEmptyState(ResolverListAdapter activeListAdapter,
@DrawableRes int iconRes, @StringRes int titleRes, @StringRes int subtitleRes,
View.OnClickListener buttonOnClick) {
+ String title = titleRes == 0 ? null : mContext.getString(titleRes);
+ String subtitle = subtitleRes == 0 ? null : mContext.getString(subtitleRes);
+ showEmptyState(activeListAdapter, iconRes, title, subtitle, buttonOnClick);
+ }
+
+ protected void showEmptyState(ResolverListAdapter activeListAdapter,
+ @DrawableRes int iconRes, String title, String subtitle,
+ View.OnClickListener buttonOnClick) {
ProfileDescriptor descriptor = getItem(
userHandleToPageIndex(activeListAdapter.getUserHandle()));
descriptor.rootView.findViewById(R.id.resolver_list).setVisibility(View.GONE);
@@ -479,15 +492,15 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter {
View container = emptyStateView.findViewById(R.id.resolver_empty_state_container);
setupContainerPadding(container);
- TextView title = emptyStateView.findViewById(R.id.resolver_empty_state_title);
- title.setText(titleRes);
+ TextView titleView = emptyStateView.findViewById(R.id.resolver_empty_state_title);
+ titleView.setText(title);
- TextView subtitle = emptyStateView.findViewById(R.id.resolver_empty_state_subtitle);
- if (subtitleRes != 0) {
- subtitle.setVisibility(View.VISIBLE);
- subtitle.setText(subtitleRes);
+ TextView subtitleView = emptyStateView.findViewById(R.id.resolver_empty_state_subtitle);
+ if (subtitle != null) {
+ subtitleView.setVisibility(View.VISIBLE);
+ subtitleView.setText(subtitle);
} else {
- subtitle.setVisibility(View.GONE);
+ subtitleView.setVisibility(View.GONE);
}
Button button = emptyStateView.findViewById(R.id.resolver_empty_state_button);
diff --git a/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
index 3b6a877907f2..393bff483a20 100644
--- a/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
@@ -16,7 +16,17 @@
package com.android.internal.app;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_ACCESS_PERSONAL;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_ACCESS_WORK;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_SHARE_WITH_PERSONAL;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_SHARE_WITH_WORK;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CROSS_PROFILE_BLOCKED_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_NO_PERSONAL_APPS;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_NO_WORK_APPS;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_WORK_PAUSED_TITLE;
+
import android.annotation.Nullable;
+import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.os.UserHandle;
import android.view.LayoutInflater;
@@ -184,8 +194,8 @@ public class ChooserMultiProfilePagerAdapter extends AbstractMultiProfilePagerAd
View.OnClickListener listener) {
showEmptyState(activeListAdapter,
R.drawable.ic_work_apps_off,
- R.string.resolver_turn_on_work_apps,
- /* subtitleRes */ 0,
+ getWorkAppPausedTitle(),
+ /* subtitle = */ null,
listener);
}
@@ -194,13 +204,13 @@ public class ChooserMultiProfilePagerAdapter extends AbstractMultiProfilePagerAd
if (mIsSendAction) {
showEmptyState(activeListAdapter,
R.drawable.ic_sharing_disabled,
- R.string.resolver_cross_profile_blocked,
- R.string.resolver_cant_share_with_work_apps_explanation);
+ getCrossProfileBlockedTitle(),
+ getCantShareWithWorkMessage());
} else {
showEmptyState(activeListAdapter,
R.drawable.ic_sharing_disabled,
- R.string.resolver_cross_profile_blocked,
- R.string.resolver_cant_access_work_apps_explanation);
+ getCrossProfileBlockedTitle(),
+ getCantAccessWorkMessage());
}
}
@@ -209,13 +219,13 @@ public class ChooserMultiProfilePagerAdapter extends AbstractMultiProfilePagerAd
if (mIsSendAction) {
showEmptyState(activeListAdapter,
R.drawable.ic_sharing_disabled,
- R.string.resolver_cross_profile_blocked,
- R.string.resolver_cant_share_with_personal_apps_explanation);
+ getCrossProfileBlockedTitle(),
+ getCantShareWithPersonalMessage());
} else {
showEmptyState(activeListAdapter,
R.drawable.ic_sharing_disabled,
- R.string.resolver_cross_profile_blocked,
- R.string.resolver_cant_access_personal_apps_explanation);
+ getCrossProfileBlockedTitle(),
+ getCantAccessPersonalMessage());
}
}
@@ -223,8 +233,8 @@ public class ChooserMultiProfilePagerAdapter extends AbstractMultiProfilePagerAd
protected void showNoPersonalAppsAvailableEmptyState(ResolverListAdapter listAdapter) {
showEmptyState(listAdapter,
R.drawable.ic_no_apps,
- R.string.resolver_no_personal_apps_available,
- /* subtitleRes */ 0);
+ getNoPersonalAppsAvailableMessage(),
+ /* subtitle= */ null);
}
@@ -232,10 +242,65 @@ public class ChooserMultiProfilePagerAdapter extends AbstractMultiProfilePagerAd
protected void showNoWorkAppsAvailableEmptyState(ResolverListAdapter listAdapter) {
showEmptyState(listAdapter,
R.drawable.ic_no_apps,
- R.string.resolver_no_work_apps_available,
- /* subtitleRes */ 0);
+ getNoWorkAppsAvailableMessage(),
+ /* subtitle = */ null);
+ }
+
+ private String getWorkAppPausedTitle() {
+ return getContext().getSystemService(DevicePolicyManager.class).getString(
+ RESOLVER_WORK_PAUSED_TITLE,
+ () -> getContext().getString(R.string.resolver_turn_on_work_apps));
+ }
+
+ private String getCrossProfileBlockedTitle() {
+ return getContext().getSystemService(DevicePolicyManager.class).getString(
+ RESOLVER_CROSS_PROFILE_BLOCKED_TITLE,
+ () -> getContext().getString(R.string.resolver_cross_profile_blocked));
+ }
+
+ private String getCantShareWithWorkMessage() {
+ return getContext().getSystemService(DevicePolicyManager.class).getString(
+ RESOLVER_CANT_SHARE_WITH_WORK,
+ () -> getContext().getString(
+ R.string.resolver_cant_share_with_work_apps_explanation));
+ }
+
+ private String getCantShareWithPersonalMessage() {
+ return getContext().getSystemService(DevicePolicyManager.class).getString(
+ RESOLVER_CANT_SHARE_WITH_PERSONAL,
+ () -> getContext().getString(
+ R.string.resolver_cant_share_with_personal_apps_explanation));
}
+ private String getCantAccessWorkMessage() {
+ return getContext().getSystemService(DevicePolicyManager.class).getString(
+ RESOLVER_CANT_ACCESS_WORK,
+ () -> getContext().getString(
+ R.string.resolver_cant_access_work_apps_explanation));
+ }
+
+ private String getCantAccessPersonalMessage() {
+ return getContext().getSystemService(DevicePolicyManager.class).getString(
+ RESOLVER_CANT_ACCESS_PERSONAL,
+ () -> getContext().getString(
+ R.string.resolver_cant_access_personal_apps_explanation));
+ }
+
+ private String getNoWorkAppsAvailableMessage() {
+ return getContext().getSystemService(DevicePolicyManager.class).getString(
+ RESOLVER_NO_WORK_APPS,
+ () -> getContext().getString(
+ R.string.resolver_no_work_apps_available));
+ }
+
+ private String getNoPersonalAppsAvailableMessage() {
+ return getContext().getSystemService(DevicePolicyManager.class).getString(
+ RESOLVER_NO_PERSONAL_APPS,
+ () -> getContext().getString(
+ R.string.resolver_no_personal_apps_available));
+ }
+
+
void setEmptyStateBottomOffset(int bottomOffset) {
mBottomOffset = bottomOffset;
}
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index 587876df0df6..9648008274ef 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -21,6 +21,7 @@ import com.android.internal.os.BatteryStatsImpl;
import android.bluetooth.BluetoothActivityEnergyInfo;
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
+import android.os.BluetoothBatteryStats;
import android.os.ParcelFileDescriptor;
import android.os.WakeLockStats;
import android.os.WorkSource;
@@ -162,6 +163,10 @@ interface IBatteryStats {
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BATTERY_STATS)")
WakeLockStats getWakeLockStats();
+ /** {@hide} */
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BATTERY_STATS)")
+ BluetoothBatteryStats getBluetoothBatteryStats();
+
HealthStatsParceler takeUidSnapshot(int uid);
HealthStatsParceler[] takeUidSnapshots(in int[] uid);
diff --git a/core/java/com/android/internal/app/IntentForwarderActivity.java b/core/java/com/android/internal/app/IntentForwarderActivity.java
index 0f37dc5949f9..25b8dba2870b 100644
--- a/core/java/com/android/internal/app/IntentForwarderActivity.java
+++ b/core/java/com/android/internal/app/IntentForwarderActivity.java
@@ -16,13 +16,14 @@
package com.android.internal.app;
+import static android.app.admin.DevicePolicyResources.Strings.Core.FORWARD_INTENT_TO_PERSONAL;
+import static android.app.admin.DevicePolicyResources.Strings.Core.FORWARD_INTENT_TO_WORK;
import static android.content.pm.PackageManager.MATCH_DEFAULT_ONLY;
import static com.android.internal.app.ResolverActivity.EXTRA_CALLING_USER;
import static com.android.internal.app.ResolverActivity.EXTRA_SELECTED_PROFILE;
import android.annotation.Nullable;
-import android.annotation.StringRes;
import android.app.Activity;
import android.app.ActivityThread;
import android.app.AppGlobals;
@@ -101,16 +102,16 @@ public class IntentForwarderActivity extends Activity {
Intent intentReceived = getIntent();
String className = intentReceived.getComponent().getClassName();
final int targetUserId;
- final int userMessageId;
+ final String userMessage;
if (className.equals(FORWARD_INTENT_TO_PARENT)) {
- userMessageId = com.android.internal.R.string.forward_intent_to_owner;
+ userMessage = getForwardToPersonalMessage();
targetUserId = getProfileParent();
getMetricsLogger().write(
new LogMaker(MetricsEvent.ACTION_SWITCH_SHARE_PROFILE)
.setSubtype(MetricsEvent.PARENT_PROFILE));
} else if (className.equals(FORWARD_INTENT_TO_MANAGED_PROFILE)) {
- userMessageId = com.android.internal.R.string.forward_intent_to_work;
+ userMessage = getForwardToWorkMessage();
targetUserId = getManagedProfile();
getMetricsLogger().write(
@@ -118,7 +119,7 @@ public class IntentForwarderActivity extends Activity {
.setSubtype(MetricsEvent.MANAGED_PROFILE));
} else {
Slog.wtf(TAG, IntentForwarderActivity.class.getName() + " cannot be called directly");
- userMessageId = -1;
+ userMessage = null;
targetUserId = UserHandle.USER_NULL;
}
if (targetUserId == UserHandle.USER_NULL) {
@@ -156,11 +157,23 @@ public class IntentForwarderActivity extends Activity {
return targetResolveInfo;
}, mExecutorService)
.thenAcceptAsync(result -> {
- maybeShowDisclosure(intentReceived, result, userMessageId);
+ maybeShowDisclosure(intentReceived, result, userMessage);
finish();
}, getApplicationContext().getMainExecutor());
}
+ private String getForwardToPersonalMessage() {
+ return getSystemService(DevicePolicyManager.class).getString(
+ FORWARD_INTENT_TO_PERSONAL,
+ () -> getString(com.android.internal.R.string.forward_intent_to_owner));
+ }
+
+ private String getForwardToWorkMessage() {
+ return getSystemService(DevicePolicyManager.class).getString(
+ FORWARD_INTENT_TO_WORK,
+ () -> getString(com.android.internal.R.string.forward_intent_to_work));
+ }
+
private boolean isIntentForwarderResolveInfo(ResolveInfo resolveInfo) {
if (resolveInfo == null) {
return false;
@@ -183,9 +196,9 @@ public class IntentForwarderActivity extends Activity {
}
private void maybeShowDisclosure(
- Intent intentReceived, ResolveInfo resolveInfo, int messageId) {
- if (shouldShowDisclosure(resolveInfo, intentReceived)) {
- mInjector.showToast(messageId, Toast.LENGTH_LONG);
+ Intent intentReceived, ResolveInfo resolveInfo, @Nullable String message) {
+ if (shouldShowDisclosure(resolveInfo, intentReceived) && message != null) {
+ mInjector.showToast(message, Toast.LENGTH_LONG);
}
}
@@ -405,8 +418,8 @@ public class IntentForwarderActivity extends Activity {
}
@Override
- public void showToast(int messageId, int duration) {
- Toast.makeText(IntentForwarderActivity.this, getString(messageId), duration).show();
+ public void showToast(String message, int duration) {
+ Toast.makeText(IntentForwarderActivity.this, message, duration).show();
}
}
@@ -419,6 +432,6 @@ public class IntentForwarderActivity extends Activity {
CompletableFuture<ResolveInfo> resolveActivityAsUser(Intent intent, int flags, int userId);
- void showToast(@StringRes int messageId, int duration);
+ void showToast(String message, int duration);
}
}
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index f9a8c7b58897..347153c7e53e 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -17,6 +17,13 @@
package com.android.internal.app;
import static android.Manifest.permission.INTERACT_ACROSS_PROFILES;
+import static android.app.admin.DevicePolicyResources.Strings.Core.FORWARD_INTENT_TO_PERSONAL;
+import static android.app.admin.DevicePolicyResources.Strings.Core.FORWARD_INTENT_TO_WORK;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_PERSONAL_TAB;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_PERSONAL_TAB_ACCESSIBILITY;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_WORK_PROFILE_NOT_SUPPORTED;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_WORK_TAB;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_WORK_TAB_ACCESSIBILITY;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.PermissionChecker.PID_UNKNOWN;
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
@@ -32,6 +39,7 @@ import android.app.VoiceInteractor.PickOptionRequest;
import android.app.VoiceInteractor.PickOptionRequest.Option;
import android.app.VoiceInteractor.Prompt;
import android.app.admin.DevicePolicyEventLogger;
+import android.app.admin.DevicePolicyManager;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -123,7 +131,7 @@ public class ResolverActivity extends Activity implements
protected View mProfileView;
private int mLastSelected = AbsListView.INVALID_POSITION;
private boolean mResolvingHome = false;
- private int mProfileSwitchMessageId = -1;
+ private String mProfileSwitchMessage;
private int mLayoutId;
@VisibleForTesting
protected final ArrayList<Intent> mIntents = new ArrayList<>();
@@ -441,7 +449,7 @@ public class ResolverActivity extends Activity implements
// Determine whether we should show that intent is forwarded
// from managed profile to owner or other way around.
- setProfileSwitchMessageId(intent.getContentUserHint());
+ setProfileSwitchMessage(intent.getContentUserHint());
mLaunchedFromUid = getLaunchedFromUid();
if (mLaunchedFromUid < 0 || UserHandle.isIsolated(mLaunchedFromUid)) {
@@ -674,7 +682,7 @@ public class ResolverActivity extends Activity implements
}
// Do not show the profile switch message anymore.
- mProfileSwitchMessageId = -1;
+ mProfileSwitchMessage = null;
onTargetSelected(dri, false);
if (!mAwaitingDelegateResponse) {
@@ -828,7 +836,7 @@ public class ResolverActivity extends Activity implements
}
}
- private void setProfileSwitchMessageId(int contentUserHint) {
+ private void setProfileSwitchMessage(int contentUserHint) {
if (contentUserHint != UserHandle.USER_CURRENT &&
contentUserHint != UserHandle.myUserId()) {
UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE);
@@ -837,13 +845,25 @@ public class ResolverActivity extends Activity implements
: false;
boolean targetIsManaged = userManager.isManagedProfile();
if (originIsManaged && !targetIsManaged) {
- mProfileSwitchMessageId = com.android.internal.R.string.forward_intent_to_owner;
+ mProfileSwitchMessage = getForwardToPersonalMsg();
} else if (!originIsManaged && targetIsManaged) {
- mProfileSwitchMessageId = com.android.internal.R.string.forward_intent_to_work;
+ mProfileSwitchMessage = getForwardToWorkMsg();
}
}
}
+ private String getForwardToPersonalMsg() {
+ return getSystemService(DevicePolicyManager.class).getString(
+ FORWARD_INTENT_TO_PERSONAL,
+ () -> getString(com.android.internal.R.string.forward_intent_to_owner));
+ }
+
+ private String getForwardToWorkMsg() {
+ return getSystemService(DevicePolicyManager.class).getString(
+ FORWARD_INTENT_TO_WORK,
+ () -> getString(com.android.internal.R.string.forward_intent_to_work));
+ }
+
/**
* Turn on launch mode that is safe to use when forwarding intents received from
* applications and running in system processes. This mode uses Activity.startActivityAsCaller
@@ -1095,9 +1115,9 @@ public class ResolverActivity extends Activity implements
ResolveInfo ri = mMultiProfilePagerAdapter.getActiveListAdapter()
.resolveInfoForPosition(which, hasIndexBeenFiltered);
if (mResolvingHome && hasManagedProfile() && !supportsManagedProfiles(ri)) {
- Toast.makeText(this, String.format(getResources().getString(
- com.android.internal.R.string.activity_resolver_work_profiles_support),
- ri.activityInfo.loadLabel(getPackageManager()).toString()),
+ Toast.makeText(this,
+ getWorkProfileNotSupportedMsg(
+ ri.activityInfo.loadLabel(getPackageManager()).toString()),
Toast.LENGTH_LONG).show();
return;
}
@@ -1128,6 +1148,15 @@ public class ResolverActivity extends Activity implements
}
}
+ private String getWorkProfileNotSupportedMsg(String launcherName) {
+ return getSystemService(DevicePolicyManager.class).getString(
+ RESOLVER_WORK_PROFILE_NOT_SUPPORTED,
+ () -> getString(
+ com.android.internal.R.string.activity_resolver_work_profiles_support,
+ launcherName),
+ launcherName);
+ }
+
/**
* Replace me in subclasses!
*/
@@ -1394,8 +1423,8 @@ public class ResolverActivity extends Activity implements
}
// If needed, show that intent is forwarded
// from managed profile to owner or other way around.
- if (mProfileSwitchMessageId != -1) {
- Toast.makeText(this, getString(mProfileSwitchMessageId), Toast.LENGTH_LONG).show();
+ if (mProfileSwitchMessage != null) {
+ Toast.makeText(this, mProfileSwitchMessage, Toast.LENGTH_LONG).show();
}
if (!mSafeForwardingMode) {
if (cti.startAsUser(this, null, user)) {
@@ -1742,12 +1771,12 @@ public class ResolverActivity extends Activity implements
viewPager.setSaveEnabled(false);
TabHost.TabSpec tabSpec = tabHost.newTabSpec(TAB_TAG_PERSONAL)
.setContent(R.id.profile_pager)
- .setIndicator(getString(R.string.resolver_personal_tab));
+ .setIndicator(getPersonalTabLabel());
tabHost.addTab(tabSpec);
tabSpec = tabHost.newTabSpec(TAB_TAG_WORK)
.setContent(R.id.profile_pager)
- .setIndicator(getString(R.string.resolver_work_tab));
+ .setIndicator(getWorkTabLabel());
tabHost.addTab(tabSpec);
TabWidget tabWidget = tabHost.getTabWidget();
@@ -1799,6 +1828,16 @@ public class ResolverActivity extends Activity implements
findViewById(R.id.resolver_tab_divider).setVisibility(View.VISIBLE);
}
+ private String getPersonalTabLabel() {
+ return getSystemService(DevicePolicyManager.class).getString(
+ RESOLVER_PERSONAL_TAB, () -> getString(R.string.resolver_personal_tab));
+ }
+
+ private String getWorkTabLabel() {
+ return getSystemService(DevicePolicyManager.class).getString(
+ RESOLVER_WORK_TAB, () -> getString(R.string.resolver_work_tab));
+ }
+
void onHorizontalSwipeStateChanged(int state) {}
private void maybeHideDivider() {
@@ -1830,8 +1869,6 @@ public class ResolverActivity extends Activity implements
}
private void resetTabsHeaderStyle(TabWidget tabWidget) {
- String workContentDescription = getString(R.string.resolver_work_tab_accessibility);
- String personalContentDescription = getString(R.string.resolver_personal_tab_accessibility);
for (int i = 0; i < tabWidget.getChildCount(); i++) {
View tabView = tabWidget.getChildAt(i);
TextView title = tabView.findViewById(android.R.id.title);
@@ -1839,14 +1876,26 @@ public class ResolverActivity extends Activity implements
title.setTextColor(getAttrColor(this, android.R.attr.textColorTertiary));
title.setTextSize(TypedValue.COMPLEX_UNIT_PX,
getResources().getDimension(R.dimen.resolver_tab_text_size));
- if (title.getText().equals(getString(R.string.resolver_personal_tab))) {
- tabView.setContentDescription(personalContentDescription);
- } else if (title.getText().equals(getString(R.string.resolver_work_tab))) {
- tabView.setContentDescription(workContentDescription);
+ if (title.getText().equals(getPersonalTabLabel())) {
+ tabView.setContentDescription(getPersonalTabAccessibilityLabel());
+ } else if (title.getText().equals(getWorkTabLabel())) {
+ tabView.setContentDescription(getWorkTabAccessibilityLabel());
}
}
}
+ private String getPersonalTabAccessibilityLabel() {
+ return getSystemService(DevicePolicyManager.class).getString(
+ RESOLVER_PERSONAL_TAB_ACCESSIBILITY,
+ () -> getString(R.string.resolver_personal_tab_accessibility));
+ }
+
+ private String getWorkTabAccessibilityLabel() {
+ return getSystemService(DevicePolicyManager.class).getString(
+ RESOLVER_WORK_TAB_ACCESSIBILITY,
+ () -> getString(R.string.resolver_work_tab_accessibility));
+ }
+
private static int getAttrColor(Context context, int attr) {
TypedArray ta = context.obtainStyledAttributes(new int[]{attr});
int colorAccent = ta.getColor(0, 0);
diff --git a/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java
index 622f1668d052..4da59a3e77d9 100644
--- a/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java
@@ -16,7 +16,15 @@
package com.android.internal.app;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_ACCESS_PERSONAL;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_ACCESS_WORK;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CROSS_PROFILE_BLOCKED_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_NO_PERSONAL_APPS;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_NO_WORK_APPS;
+import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_WORK_PAUSED_TITLE;
+
import android.annotation.Nullable;
+import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.res.Resources;
import android.os.UserHandle;
@@ -196,8 +204,8 @@ public class ResolverMultiProfilePagerAdapter extends AbstractMultiProfilePagerA
View.OnClickListener listener) {
showEmptyState(activeListAdapter,
R.drawable.ic_work_apps_off,
- R.string.resolver_turn_on_work_apps,
- /* subtitleRes */ 0,
+ getWorkAppPausedTitle(),
+ /* subtitle = */ null,
listener);
}
@@ -205,32 +213,72 @@ public class ResolverMultiProfilePagerAdapter extends AbstractMultiProfilePagerA
protected void showNoPersonalToWorkIntentsEmptyState(ResolverListAdapter activeListAdapter) {
showEmptyState(activeListAdapter,
R.drawable.ic_sharing_disabled,
- R.string.resolver_cross_profile_blocked,
- R.string.resolver_cant_access_work_apps_explanation);
+ getCrossProfileBlockedTitle(),
+ getCantAccessWorkMessage());
}
@Override
protected void showNoWorkToPersonalIntentsEmptyState(ResolverListAdapter activeListAdapter) {
showEmptyState(activeListAdapter,
R.drawable.ic_sharing_disabled,
- R.string.resolver_cross_profile_blocked,
- R.string.resolver_cant_access_personal_apps_explanation);
+ getCrossProfileBlockedTitle(),
+ getCantAccessPersonalMessage());
}
@Override
protected void showNoPersonalAppsAvailableEmptyState(ResolverListAdapter listAdapter) {
showEmptyState(listAdapter,
R.drawable.ic_no_apps,
- R.string.resolver_no_personal_apps_available,
- /* subtitleRes */ 0);
+ getNoPersonalAppsAvailableMessage(),
+ /* subtitle = */ null);
}
@Override
protected void showNoWorkAppsAvailableEmptyState(ResolverListAdapter listAdapter) {
showEmptyState(listAdapter,
R.drawable.ic_no_apps,
- R.string.resolver_no_work_apps_available,
- /* subtitleRes */ 0);
+ getNoWorkAppsAvailableMessage(),
+ /* subtitle= */ null);
+ }
+
+ private String getWorkAppPausedTitle() {
+ return getContext().getSystemService(DevicePolicyManager.class).getString(
+ RESOLVER_WORK_PAUSED_TITLE,
+ () -> getContext().getString(R.string.resolver_turn_on_work_apps));
+ }
+
+ private String getCrossProfileBlockedTitle() {
+ return getContext().getSystemService(DevicePolicyManager.class).getString(
+ RESOLVER_CROSS_PROFILE_BLOCKED_TITLE,
+ () -> getContext().getString(R.string.resolver_cross_profile_blocked));
+ }
+
+ private String getCantAccessWorkMessage() {
+ return getContext().getSystemService(DevicePolicyManager.class).getString(
+ RESOLVER_CANT_ACCESS_WORK,
+ () -> getContext().getString(
+ R.string.resolver_cant_access_work_apps_explanation));
+ }
+
+ private String getCantAccessPersonalMessage() {
+ return getContext().getSystemService(DevicePolicyManager.class).getString(
+ RESOLVER_CANT_ACCESS_PERSONAL,
+ () -> getContext().getString(
+ R.string.resolver_cant_access_personal_apps_explanation));
+ }
+
+ private String getNoWorkAppsAvailableMessage() {
+ return getContext().getSystemService(DevicePolicyManager.class).getString(
+ RESOLVER_NO_WORK_APPS,
+ () -> getContext().getString(
+ R.string.resolver_no_work_apps_available));
+ }
+
+ private String getNoPersonalAppsAvailableMessage() {
+ return getContext().getSystemService(DevicePolicyManager.class).getString(
+ RESOLVER_NO_PERSONAL_APPS,
+ () -> getContext().getString(
+ R.string.resolver_no_personal_apps_available));
}
void setUseLayoutWithDefault(boolean useLayoutWithDefault) {
diff --git a/core/java/com/android/internal/app/UnlaunchableAppActivity.java b/core/java/com/android/internal/app/UnlaunchableAppActivity.java
index ca0856238b90..3531fad353db 100644
--- a/core/java/com/android/internal/app/UnlaunchableAppActivity.java
+++ b/core/java/com/android/internal/app/UnlaunchableAppActivity.java
@@ -16,11 +16,14 @@
package com.android.internal.app;
+import static android.app.admin.DevicePolicyResources.Strings.Core.UNLAUNCHABLE_APP_WORK_PAUSED_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.UNLAUNCHABLE_APP_WORK_PAUSED_TITLE;
import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import android.app.Activity;
import android.app.AlertDialog;
+import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.content.DialogInterface;
import android.content.Intent;
@@ -70,8 +73,8 @@ public class UnlaunchableAppActivity extends Activity
String dialogTitle;
String dialogMessage = null;
if (mReason == UNLAUNCHABLE_REASON_QUIET_MODE) {
- dialogTitle = getResources().getString(R.string.work_mode_off_title);
- dialogMessage = getResources().getString(R.string.work_mode_off_message);
+ dialogTitle = getDialogTitle();
+ dialogMessage = getDialogMessage();
} else {
Log.wtf(TAG, "Invalid unlaunchable type: " + mReason);
finish();
@@ -91,6 +94,17 @@ public class UnlaunchableAppActivity extends Activity
builder.show();
}
+ private String getDialogTitle() {
+ return getSystemService(DevicePolicyManager.class).getString(
+ UNLAUNCHABLE_APP_WORK_PAUSED_TITLE, () -> getString(R.string.work_mode_off_title));
+ }
+
+ private String getDialogMessage() {
+ return getSystemService(DevicePolicyManager.class).getString(
+ UNLAUNCHABLE_APP_WORK_PAUSED_MESSAGE,
+ () -> getString(R.string.work_mode_off_message));
+ }
+
@Override
public void onDismiss(DialogInterface dialog) {
finish();
diff --git a/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java b/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java
index 9c3c22451c5a..301de2d3529e 100644
--- a/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java
+++ b/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java
@@ -237,12 +237,12 @@ public class DisplayResolveInfo implements TargetInfo, Parcelable {
private DisplayResolveInfo(Parcel in) {
mDisplayLabel = in.readCharSequence();
mExtendedInfo = in.readCharSequence();
- mResolvedIntent = in.readParcelable(null /* ClassLoader */);
+ mResolvedIntent = in.readParcelable(null /* ClassLoader */, android.content.Intent.class);
mSourceIntents.addAll(
Arrays.asList((Intent[]) in.readParcelableArray(null /* ClassLoader */,
Intent.class)));
mIsSuspended = in.readBoolean();
mPinned = in.readBoolean();
- mResolveInfo = in.readParcelable(null /* ClassLoader */);
+ mResolveInfo = in.readParcelable(null /* ClassLoader */, android.content.pm.ResolveInfo.class);
}
}
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index 13a39de3e365..0ada13a73ad2 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -163,6 +163,12 @@ public final class SystemUiDeviceConfigFlags {
public static final String PROPERTY_LOCATION_INDICATORS_SMALL_ENABLED =
"location_indicators_small_enabled";
+ /**
+ * Whether to show the location indicator for system apps.
+ */
+ public static final String PROPERTY_LOCATION_INDICATORS_SHOW_SYSTEM =
+ "location_indicators_show_system";
+
// Flags related to Assistant
/**
diff --git a/core/java/com/android/internal/graphics/ColorUtils.java b/core/java/com/android/internal/graphics/ColorUtils.java
index 537e797c9bac..dff9551c0c07 100644
--- a/core/java/com/android/internal/graphics/ColorUtils.java
+++ b/core/java/com/android/internal/graphics/ColorUtils.java
@@ -62,7 +62,10 @@ public final class ColorUtils {
return Color.argb(a, r, g, b);
}
- private static int compositeAlpha(int foregroundAlpha, int backgroundAlpha) {
+ /**
+ * Returns the composite alpha of the given foreground and background alpha.
+ */
+ public static int compositeAlpha(int foregroundAlpha, int backgroundAlpha) {
return 0xFF - (((0xFF - backgroundAlpha) * (0xFF - foregroundAlpha)) / 0xFF);
}
diff --git a/core/java/com/android/internal/logging/UiEventLogger.java b/core/java/com/android/internal/logging/UiEventLogger.java
index 2f4a14fad2ee..5378b03fe1c5 100644
--- a/core/java/com/android/internal/logging/UiEventLogger.java
+++ b/core/java/com/android/internal/logging/UiEventLogger.java
@@ -58,6 +58,14 @@ public interface UiEventLogger {
void log(@NonNull UiEventEnum event);
/**
+ * Log a simple event with an instance id, without package information.
+ * Does nothing if event.getId() <= 0.
+ * @param event an enum implementing UiEventEnum interface.
+ * @param instance An identifier obtained from an InstanceIdSequence. If null, reduces to log().
+ */
+ void log(@NonNull UiEventEnum event, @Nullable InstanceId instance);
+
+ /**
* Log an event with package information. Does nothing if event.getId() <= 0.
* Give both uid and packageName if both are known, but one may be omitted if unknown.
* @param event an enum implementing UiEventEnum interface.
diff --git a/core/java/com/android/internal/logging/UiEventLoggerImpl.java b/core/java/com/android/internal/logging/UiEventLoggerImpl.java
index c0f44a5eb39b..983e0fe6144e 100644
--- a/core/java/com/android/internal/logging/UiEventLoggerImpl.java
+++ b/core/java/com/android/internal/logging/UiEventLoggerImpl.java
@@ -42,16 +42,21 @@ public class UiEventLoggerImpl implements UiEventLogger {
}
@Override
+ public void log(UiEventEnum event, InstanceId instanceId) {
+ logWithInstanceId(event, 0, null, instanceId);
+ }
+
+ @Override
public void logWithInstanceId(UiEventEnum event, int uid, String packageName,
InstanceId instance) {
final int eventID = event.getId();
- if ((eventID > 0) && (instance != null)) {
+ if ((eventID > 0) && (instance != null)) {
FrameworkStatsLog.write(FrameworkStatsLog.UI_EVENT_REPORTED,
/* event_id = 1 */ eventID,
/* uid = 2 */ uid,
/* package_name = 3 */ packageName,
/* instance_id = 4 */ instance.getId());
- } else {
+ } else if (eventID > 0) {
log(event, uid, packageName);
}
}
@@ -78,7 +83,7 @@ public class UiEventLoggerImpl implements UiEventLogger {
/* package_name = 2 */ packageName,
/* instance_id = 3 */ instance.getId(),
/* position_picked = 4 */ position);
- } else {
+ } else if ((eventID > 0)) {
logWithPosition(event, uid, packageName, position);
}
}
diff --git a/core/java/com/android/internal/logging/testing/UiEventLoggerFake.java b/core/java/com/android/internal/logging/testing/UiEventLoggerFake.java
index 2d09434807a6..e303890c245a 100644
--- a/core/java/com/android/internal/logging/testing/UiEventLoggerFake.java
+++ b/core/java/com/android/internal/logging/testing/UiEventLoggerFake.java
@@ -88,6 +88,11 @@ public class UiEventLoggerFake implements UiEventLogger {
}
@Override
+ public void log(UiEventEnum event, InstanceId instance) {
+ logWithInstanceId(event, 0, null, instance);
+ }
+
+ @Override
public void log(UiEventEnum event, int uid, String packageName) {
final int eventId = event.getId();
if (eventId > 0) {
diff --git a/core/java/com/android/internal/midi/MidiFramer.java b/core/java/com/android/internal/midi/MidiFramer.java
index 62517fa82953..bf23ad190ef1 100644
--- a/core/java/com/android/internal/midi/MidiFramer.java
+++ b/core/java/com/android/internal/midi/MidiFramer.java
@@ -99,6 +99,12 @@ public class MidiFramer extends MidiReceiver {
}
} else { // data byte
if (!mInSysEx) {
+ // Hack to avoid crashing if we start parsing in the middle
+ // of a data stream
+ if (mNeeded <= 0) {
+ break;
+ }
+
mBuffer[mCount++] = currentByte;
if (--mNeeded == 0) {
if (mRunningStatus != 0) {
diff --git a/core/java/com/android/internal/midi/OWNERS b/core/java/com/android/internal/midi/OWNERS
new file mode 100644
index 000000000000..af273a6f50e0
--- /dev/null
+++ b/core/java/com/android/internal/midi/OWNERS
@@ -0,0 +1 @@
+include /services/midi/OWNERS
diff --git a/core/java/com/android/internal/net/LegacyVpnInfo.java b/core/java/com/android/internal/net/LegacyVpnInfo.java
index 43984b59378c..b3bc93a058cf 100644
--- a/core/java/com/android/internal/net/LegacyVpnInfo.java
+++ b/core/java/com/android/internal/net/LegacyVpnInfo.java
@@ -69,7 +69,7 @@ public class LegacyVpnInfo implements Parcelable {
LegacyVpnInfo info = new LegacyVpnInfo();
info.key = in.readString();
info.state = in.readInt();
- info.intent = in.readParcelable(null);
+ info.intent = in.readParcelable(null, android.app.PendingIntent.class);
return info;
}
diff --git a/core/java/com/android/internal/net/VpnConfig.java b/core/java/com/android/internal/net/VpnConfig.java
index 2ae56f808972..b579be03acbd 100644
--- a/core/java/com/android/internal/net/VpnConfig.java
+++ b/core/java/com/android/internal/net/VpnConfig.java
@@ -208,7 +208,7 @@ public class VpnConfig implements Parcelable {
config.searchDomains = in.createStringArrayList();
config.allowedApplications = in.createStringArrayList();
config.disallowedApplications = in.createStringArrayList();
- config.configureIntent = in.readParcelable(null);
+ config.configureIntent = in.readParcelable(null, android.app.PendingIntent.class);
config.startTime = in.readLong();
config.legacy = in.readInt() != 0;
config.blocking = in.readInt() != 0;
@@ -217,7 +217,7 @@ public class VpnConfig implements Parcelable {
config.allowIPv6 = in.readInt() != 0;
config.isMetered = in.readInt() != 0;
config.underlyingNetworks = in.createTypedArray(Network.CREATOR);
- config.proxyInfo = in.readParcelable(null);
+ config.proxyInfo = in.readParcelable(null, android.net.ProxyInfo.class);
return config;
}
diff --git a/core/java/com/android/internal/net/VpnProfile.java b/core/java/com/android/internal/net/VpnProfile.java
index d8dc1436128e..519faa8456cc 100644
--- a/core/java/com/android/internal/net/VpnProfile.java
+++ b/core/java/com/android/internal/net/VpnProfile.java
@@ -182,9 +182,9 @@ public final class VpnProfile implements Cloneable, Parcelable {
ipsecCaCert = in.readString();
ipsecServerCert = in.readString();
saveLogin = in.readInt() != 0;
- proxy = in.readParcelable(null);
+ proxy = in.readParcelable(null, android.net.ProxyInfo.class);
mAllowedAlgorithms = new ArrayList<>();
- in.readList(mAllowedAlgorithms, null);
+ in.readList(mAllowedAlgorithms, null, java.lang.String.class);
isBypassable = in.readBoolean();
isMetered = in.readBoolean();
maxMtu = in.readInt();
diff --git a/core/java/com/android/internal/notification/SystemNotificationChannels.java b/core/java/com/android/internal/notification/SystemNotificationChannels.java
index 2f40d3b457c6..3b6f8f6187e1 100644
--- a/core/java/com/android/internal/notification/SystemNotificationChannels.java
+++ b/core/java/com/android/internal/notification/SystemNotificationChannels.java
@@ -14,10 +14,13 @@
package com.android.internal.notification;
+import static android.app.admin.DevicePolicyResources.Strings.Core.NOTIFICATION_CHANNEL_DEVICE_ADMIN;
+
import android.app.INotificationManager;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
+import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.pm.ParceledListSlice;
import android.media.AudioAttributes;
@@ -143,7 +146,7 @@ public class SystemNotificationChannels {
final NotificationChannel deviceAdmin = new NotificationChannel(
DEVICE_ADMIN,
- context.getString(R.string.notification_channel_device_admin),
+ getDeviceAdminNotificationChannelName(context),
NotificationManager.IMPORTANCE_HIGH);
channelsList.add(deviceAdmin);
@@ -209,6 +212,12 @@ public class SystemNotificationChannels {
nm.createNotificationChannels(channelsList);
}
+ private static String getDeviceAdminNotificationChannelName(Context context) {
+ DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
+ return dpm.getString(NOTIFICATION_CHANNEL_DEVICE_ADMIN,
+ () -> context.getString(R.string.notification_channel_device_admin));
+ }
+
/** Remove notification channels which are no longer used */
public static void removeDeprecated(Context context) {
final NotificationManager nm = context.getSystemService(NotificationManager.class);
diff --git a/core/java/com/android/internal/os/AppFuseMount.java b/core/java/com/android/internal/os/AppFuseMount.java
index 04d72117d28a..5404fea68672 100644
--- a/core/java/com/android/internal/os/AppFuseMount.java
+++ b/core/java/com/android/internal/os/AppFuseMount.java
@@ -57,7 +57,7 @@ public class AppFuseMount implements Parcelable {
new Parcelable.Creator<AppFuseMount>() {
@Override
public AppFuseMount createFromParcel(Parcel in) {
- return new AppFuseMount(in.readInt(), in.readParcelable(null));
+ return new AppFuseMount(in.readInt(), in.readParcelable(null, android.os.ParcelFileDescriptor.class));
}
@Override
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index a4183ca8f163..8213c863875c 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -45,6 +45,7 @@ import android.os.BatteryConsumer;
import android.os.BatteryManager;
import android.os.BatteryStats;
import android.os.Binder;
+import android.os.BluetoothBatteryStats;
import android.os.Build;
import android.os.Handler;
import android.os.IBatteryPropertiesRegistrar;
@@ -84,7 +85,6 @@ import android.util.Log;
import android.util.LongSparseArray;
import android.util.LongSparseLongArray;
import android.util.MutableInt;
-import android.util.Pools;
import android.util.PrintWriterPrinter;
import android.util.Printer;
import android.util.Slog;
@@ -137,9 +137,7 @@ import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
-import java.util.Objects;
import java.util.Queue;
-import java.util.Set;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;
@@ -265,6 +263,7 @@ public class BatteryStatsImpl extends BatteryStats {
MeasuredEnergyStats.POWER_BUCKET_CPU,
MeasuredEnergyStats.POWER_BUCKET_MOBILE_RADIO,
MeasuredEnergyStats.POWER_BUCKET_WIFI,
+ MeasuredEnergyStats.POWER_BUCKET_BLUETOOTH,
};
// TimeInState counters need NUM_PROCESS_STATE states in order to accommodate
@@ -1198,6 +1197,48 @@ public class BatteryStatsImpl extends BatteryStats {
return new WakeLockStats(uidWakeLockStats);
}
+ @Override
+ @GuardedBy("this")
+ public BluetoothBatteryStats getBluetoothBatteryStats() {
+ final long elapsedRealtimeUs = mClock.elapsedRealtime() * 1000;
+ ArrayList<BluetoothBatteryStats.UidStats> uidStats = new ArrayList<>();
+ for (int i = mUidStats.size() - 1; i >= 0; i--) {
+ final Uid uid = mUidStats.valueAt(i);
+ final Timer scanTimer = uid.getBluetoothScanTimer();
+ final long scanTimeMs =
+ scanTimer != null ? scanTimer.getTotalTimeLocked(
+ elapsedRealtimeUs, STATS_SINCE_CHARGED) / 1000 : 0;
+
+ final Timer unoptimizedScanTimer = uid.getBluetoothUnoptimizedScanTimer();
+ final long unoptimizedScanTimeMs =
+ unoptimizedScanTimer != null ? unoptimizedScanTimer.getTotalTimeLocked(
+ elapsedRealtimeUs, STATS_SINCE_CHARGED) / 1000 : 0;
+
+ final Counter scanResultCounter = uid.getBluetoothScanResultCounter();
+ final int scanResultCount =
+ scanResultCounter != null ? scanResultCounter.getCountLocked(
+ STATS_SINCE_CHARGED) : 0;
+
+ final ControllerActivityCounter counter = uid.getBluetoothControllerActivity();
+ final long rxTimeMs = counter != null ? counter.getRxTimeCounter().getCountLocked(
+ STATS_SINCE_CHARGED) : 0;
+ final long txTimeMs = counter != null ? counter.getTxTimeCounters()[0].getCountLocked(
+ STATS_SINCE_CHARGED) : 0;
+
+ if (scanTimeMs != 0 || unoptimizedScanTimeMs != 0 || scanResultCount != 0
+ || rxTimeMs != 0 || txTimeMs != 0) {
+ uidStats.add(new BluetoothBatteryStats.UidStats(uid.getUid(),
+ scanTimeMs,
+ unoptimizedScanTimeMs,
+ scanResultCount,
+ rxTimeMs,
+ txTimeMs));
+ }
+ }
+
+ return new BluetoothBatteryStats(uidStats);
+ }
+
String mLastWakeupReason = null;
long mLastWakeupUptimeMs = 0;
private final HashMap<String, SamplingTimer> mWakeupReasonStats = new HashMap<>();
@@ -8371,6 +8412,11 @@ public class BatteryStatsImpl extends BatteryStats {
if (wifiControllerActivity != null) {
wifiControllerActivity.setState(batteryConsumerProcessState, elapsedTimeMs);
}
+ final ControllerActivityCounterImpl bluetoothControllerActivity =
+ getBluetoothControllerActivity();
+ if (bluetoothControllerActivity != null) {
+ bluetoothControllerActivity.setState(batteryConsumerProcessState, elapsedTimeMs);
+ }
final MeasuredEnergyStats energyStats =
getOrCreateMeasuredEnergyStatsIfSupportedLocked();
if (energyStats != null) {
@@ -8718,7 +8764,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
@Override
- public ControllerActivityCounter getBluetoothControllerActivity() {
+ public ControllerActivityCounterImpl getBluetoothControllerActivity() {
return mBluetoothControllerActivity;
}
@@ -8839,6 +8885,14 @@ public class BatteryStatsImpl extends BatteryStats {
@GuardedBy("mBsi")
@Override
+ public long getBluetoothMeasuredBatteryConsumptionUC(
+ @BatteryConsumer.ProcessState int processState) {
+ return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_BLUETOOTH,
+ processState);
+ }
+
+ @GuardedBy("mBsi")
+ @Override
public long getCpuMeasuredBatteryConsumptionUC() {
return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_CPU);
}
@@ -11424,6 +11478,13 @@ public class BatteryStatsImpl extends BatteryStats {
wifiControllerActivity.setState(batteryConsumerProcessState, elapsedRealtimeMs);
}
+ final ControllerActivityCounterImpl bluetoothControllerActivity =
+ getBluetoothControllerActivity();
+ if (bluetoothControllerActivity != null) {
+ bluetoothControllerActivity.setState(batteryConsumerProcessState,
+ elapsedRealtimeMs);
+ }
+
final MeasuredEnergyStats energyStats =
getOrCreateMeasuredEnergyStatsIfSupportedLocked();
if (energyStats != null) {
@@ -12669,8 +12730,6 @@ public class BatteryStatsImpl extends BatteryStats {
}
}
- private final Pools.Pool<NetworkStats> mNetworkStatsPool = new Pools.SynchronizedPool<>(6);
-
private final Object mWifiNetworkLock = new Object();
@GuardedBy("mWifiNetworkLock")
@@ -12688,13 +12747,15 @@ public class BatteryStatsImpl extends BatteryStats {
private NetworkStats mLastModemNetworkStats = new NetworkStats(0, -1);
@VisibleForTesting
- protected NetworkStats readNetworkStatsLocked(@NonNull NetworkStatsManager networkStatsManager,
- String[] ifaces) {
- Objects.requireNonNull(networkStatsManager);
- if (!ArrayUtils.isEmpty(ifaces)) {
- return networkStatsManager.getDetailedUidStats(Set.of(ifaces));
- }
- return null;
+ protected NetworkStats readMobileNetworkStatsLocked(
+ @NonNull NetworkStatsManager networkStatsManager) {
+ return networkStatsManager.getMobileUidStats();
+ }
+
+ @VisibleForTesting
+ protected NetworkStats readWifiNetworkStatsLocked(
+ @NonNull NetworkStatsManager networkStatsManager) {
+ return networkStatsManager.getWifiUidStats();
}
/**
@@ -12714,21 +12775,15 @@ public class BatteryStatsImpl extends BatteryStats {
// Grab a separate lock to acquire the network stats, which may do I/O.
NetworkStats delta = null;
synchronized (mWifiNetworkLock) {
- final NetworkStats latestStats = readNetworkStatsLocked(networkStatsManager,
- mWifiIfaces);
+ final NetworkStats latestStats = readWifiNetworkStatsLocked(networkStatsManager);
if (latestStats != null) {
- delta = NetworkStats.subtract(latestStats, mLastWifiNetworkStats, null, null,
- mNetworkStatsPool.acquire());
- mNetworkStatsPool.release(mLastWifiNetworkStats);
+ delta = latestStats.subtract(mLastWifiNetworkStats);
mLastWifiNetworkStats = latestStats;
}
}
synchronized (this) {
if (!mOnBatteryInternal || mIgnoreNextExternalStats) {
- if (delta != null) {
- mNetworkStatsPool.release(delta);
- }
if (mIgnoreNextExternalStats) {
// TODO: Strictly speaking, we should re-mark all 5 timers for each uid (and the
// global one) here like we do for display. But I'm not sure it's worth the
@@ -12746,63 +12801,63 @@ public class BatteryStatsImpl extends BatteryStats {
SparseLongArray rxPackets = new SparseLongArray();
SparseLongArray txPackets = new SparseLongArray();
+ SparseLongArray rxTimesMs = new SparseLongArray();
+ SparseLongArray txTimesMs = new SparseLongArray();
long totalTxPackets = 0;
long totalRxPackets = 0;
if (delta != null) {
- NetworkStats.Entry entry = new NetworkStats.Entry();
- final int size = delta.size();
- for (int i = 0; i < size; i++) {
- entry = delta.getValues(i, entry);
-
+ for (NetworkStats.Entry entry : delta) {
if (DEBUG_ENERGY) {
- Slog.d(TAG, "Wifi uid " + entry.uid + ": delta rx=" + entry.rxBytes
- + " tx=" + entry.txBytes + " rxPackets=" + entry.rxPackets
- + " txPackets=" + entry.txPackets);
+ Slog.d(TAG, "Wifi uid " + entry.getUid()
+ + ": delta rx=" + entry.getRxBytes()
+ + " tx=" + entry.getTxBytes()
+ + " rxPackets=" + entry.getRxPackets()
+ + " txPackets=" + entry.getTxPackets());
}
- if (entry.rxBytes == 0 && entry.txBytes == 0) {
+ if (entry.getRxBytes() == 0 && entry.getTxBytes() == 0) {
// Skip the lookup below since there is no work to do.
continue;
}
- final int uid = mapUid(entry.uid);
+ final int uid = mapUid(entry.getUid());
final Uid u = getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs);
- if (entry.rxBytes != 0) {
- u.noteNetworkActivityLocked(NETWORK_WIFI_RX_DATA, entry.rxBytes,
- entry.rxPackets);
- if (entry.set == NetworkStats.SET_DEFAULT) { // Background transfers
- u.noteNetworkActivityLocked(NETWORK_WIFI_BG_RX_DATA, entry.rxBytes,
- entry.rxPackets);
+ if (entry.getRxBytes() != 0) {
+ u.noteNetworkActivityLocked(NETWORK_WIFI_RX_DATA, entry.getRxBytes(),
+ entry.getRxPackets());
+ if (entry.getSet() == NetworkStats.SET_DEFAULT) { // Background transfers
+ u.noteNetworkActivityLocked(NETWORK_WIFI_BG_RX_DATA, entry.getRxBytes(),
+ entry.getRxPackets());
}
mNetworkByteActivityCounters[NETWORK_WIFI_RX_DATA].addCountLocked(
- entry.rxBytes);
+ entry.getRxBytes());
mNetworkPacketActivityCounters[NETWORK_WIFI_RX_DATA].addCountLocked(
- entry.rxPackets);
+ entry.getRxPackets());
- add(rxPackets, uid, entry.rxPackets);
+ rxPackets.incrementValue(uid, entry.getRxPackets());
// Sum the total number of packets so that the Rx Power can
// be evenly distributed amongst the apps.
- totalRxPackets += entry.rxPackets;
+ totalRxPackets += entry.getRxPackets();
}
- if (entry.txBytes != 0) {
- u.noteNetworkActivityLocked(NETWORK_WIFI_TX_DATA, entry.txBytes,
- entry.txPackets);
- if (entry.set == NetworkStats.SET_DEFAULT) { // Background transfers
- u.noteNetworkActivityLocked(NETWORK_WIFI_BG_TX_DATA, entry.txBytes,
- entry.txPackets);
+ if (entry.getTxBytes() != 0) {
+ u.noteNetworkActivityLocked(NETWORK_WIFI_TX_DATA, entry.getTxBytes(),
+ entry.getTxPackets());
+ if (entry.getSet() == NetworkStats.SET_DEFAULT) { // Background transfers
+ u.noteNetworkActivityLocked(NETWORK_WIFI_BG_TX_DATA, entry.getTxBytes(),
+ entry.getTxPackets());
}
mNetworkByteActivityCounters[NETWORK_WIFI_TX_DATA].addCountLocked(
- entry.txBytes);
+ entry.getTxBytes());
mNetworkPacketActivityCounters[NETWORK_WIFI_TX_DATA].addCountLocked(
- entry.txPackets);
+ entry.getTxPackets());
- add(txPackets, uid, entry.txPackets);
+ txPackets.incrementValue(uid, entry.getTxPackets());
// Sum the total number of packets so that the Tx Power can
// be evenly distributed amongst the apps.
- totalTxPackets += entry.txPackets;
+ totalTxPackets += entry.getTxPackets();
}
// Calculate consumed energy for this uid. Only do so if WifiReporting isn't
@@ -12828,13 +12883,12 @@ public class BatteryStatsImpl extends BatteryStats {
}
}
- uidEstimatedConsumptionMah.add(u.getUid(),
+ uidEstimatedConsumptionMah.incrementValue(u.getUid(),
mWifiPowerCalculator.calcPowerWithoutControllerDataMah(
- entry.rxPackets, entry.txPackets,
+ entry.getRxPackets(), entry.getTxPackets(),
uidRunningMs, uidScanMs, uidBatchScanMs));
}
}
- mNetworkStatsPool.release(delta);
delta = null;
}
@@ -12923,12 +12977,9 @@ public class BatteryStatsImpl extends BatteryStats {
+ scanTxTimeSinceMarkMs + " ms)");
}
- ControllerActivityCounterImpl activityCounter =
- uid.getOrCreateWifiControllerActivityLocked();
- activityCounter.getOrCreateRxTimeCounter()
- .increment(scanRxTimeSinceMarkMs, elapsedRealtimeMs);
- activityCounter.getOrCreateTxTimeCounters()[0]
- .increment(scanTxTimeSinceMarkMs, elapsedRealtimeMs);
+ rxTimesMs.incrementValue(uid.getUid(), scanRxTimeSinceMarkMs);
+ txTimesMs.incrementValue(uid.getUid(), scanTxTimeSinceMarkMs);
+
leftOverRxTimeMs -= scanRxTimeSinceMarkMs;
leftOverTxTimeMs -= scanTxTimeSinceMarkMs;
}
@@ -12955,7 +13006,7 @@ public class BatteryStatsImpl extends BatteryStats {
if (uidEstimatedConsumptionMah != null) {
double uidEstMah = mWifiPowerCalculator.calcPowerFromControllerDataMah(
scanRxTimeSinceMarkMs, scanTxTimeSinceMarkMs, myIdleTimeMs);
- uidEstimatedConsumptionMah.add(uid.getUid(), uidEstMah);
+ uidEstimatedConsumptionMah.incrementValue(uid.getUid(), uidEstMah);
}
}
@@ -12967,36 +13018,51 @@ public class BatteryStatsImpl extends BatteryStats {
// Distribute the remaining Tx power appropriately between all apps that transmitted
// packets.
for (int i = 0; i < txPackets.size(); i++) {
- final Uid uid = getUidStatsLocked(txPackets.keyAt(i),
- elapsedRealtimeMs, uptimeMs);
+ final int uid = txPackets.keyAt(i);
final long myTxTimeMs = (txPackets.valueAt(i) * leftOverTxTimeMs)
/ totalTxPackets;
+ txTimesMs.incrementValue(uid, myTxTimeMs);
+ }
+
+ // Distribute the remaining Rx power appropriately between all apps that received
+ // packets.
+ for (int i = 0; i < rxPackets.size(); i++) {
+ final int uid = rxPackets.keyAt(i);
+ final long myRxTimeMs = (rxPackets.valueAt(i) * leftOverRxTimeMs)
+ / totalRxPackets;
+ rxTimesMs.incrementValue(uid, myRxTimeMs);
+ }
+
+ for (int i = 0; i < txTimesMs.size(); i++) {
+ final int uid = txTimesMs.keyAt(i);
+ final long myTxTimeMs = txTimesMs.valueAt(i);
if (DEBUG_ENERGY) {
- Slog.d(TAG, " TxTime for UID " + uid.getUid() + ": " + myTxTimeMs + " ms");
+ Slog.d(TAG, " TxTime for UID " + uid + ": " + myTxTimeMs + " ms");
}
- uid.getOrCreateWifiControllerActivityLocked().getOrCreateTxTimeCounters()[0]
+ getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
+ .getOrCreateWifiControllerActivityLocked()
+ .getOrCreateTxTimeCounters()[0]
.increment(myTxTimeMs, elapsedRealtimeMs);
if (uidEstimatedConsumptionMah != null) {
- uidEstimatedConsumptionMah.add(uid.getUid(),
+ uidEstimatedConsumptionMah.incrementValue(uid,
mWifiPowerCalculator.calcPowerFromControllerDataMah(
0, myTxTimeMs, 0));
}
}
- // Distribute the remaining Rx power appropriately between all apps that received
- // packets.
- for (int i = 0; i < rxPackets.size(); i++) {
- final Uid uid = getUidStatsLocked(rxPackets.keyAt(i),
- elapsedRealtimeMs, uptimeMs);
- final long myRxTimeMs = (rxPackets.valueAt(i) * leftOverRxTimeMs)
- / totalRxPackets;
+ for (int i = 0; i < rxTimesMs.size(); i++) {
+ final int uid = rxTimesMs.keyAt(i);
+ final long myRxTimeMs = rxTimesMs.valueAt(i);
if (DEBUG_ENERGY) {
- Slog.d(TAG, " RxTime for UID " + uid.getUid() + ": " + myRxTimeMs + " ms");
+ Slog.d(TAG, " RxTime for UID " + uid + ": " + myRxTimeMs + " ms");
}
- uid.getOrCreateWifiControllerActivityLocked().getOrCreateRxTimeCounter()
+
+ getUidStatsLocked(rxTimesMs.keyAt(i), elapsedRealtimeMs, uptimeMs)
+ .getOrCreateWifiControllerActivityLocked()
+ .getOrCreateRxTimeCounter()
.increment(myRxTimeMs, elapsedRealtimeMs);
if (uidEstimatedConsumptionMah != null) {
- uidEstimatedConsumptionMah.add(uid.getUid(),
+ uidEstimatedConsumptionMah.incrementValue(uid,
mWifiPowerCalculator.calcPowerFromControllerDataMah(
myRxTimeMs, 0, 0));
}
@@ -13004,7 +13070,6 @@ public class BatteryStatsImpl extends BatteryStats {
// Any left over power use will be picked up by the WiFi category in BatteryStatsHelper.
-
// Update WiFi controller stats.
mWifiActivity.getOrCreateRxTimeCounter().increment(
info.getControllerRxDurationMillis(), elapsedRealtimeMs);
@@ -13083,21 +13148,15 @@ public class BatteryStatsImpl extends BatteryStats {
// Grab a separate lock to acquire the network stats, which may do I/O.
NetworkStats delta = null;
synchronized (mModemNetworkLock) {
- final NetworkStats latestStats = readNetworkStatsLocked(networkStatsManager,
- mModemIfaces);
+ final NetworkStats latestStats = readMobileNetworkStatsLocked(networkStatsManager);
if (latestStats != null) {
- delta = NetworkStats.subtract(latestStats, mLastModemNetworkStats, null, null,
- mNetworkStatsPool.acquire());
- mNetworkStatsPool.release(mLastModemNetworkStats);
+ delta = latestStats.subtract(mLastModemNetworkStats);
mLastModemNetworkStats = latestStats;
}
}
synchronized (this) {
if (!mOnBatteryInternal || mIgnoreNextExternalStats) {
- if (delta != null) {
- mNetworkStatsPool.release(delta);
- }
return;
}
@@ -13224,7 +13283,7 @@ public class BatteryStatsImpl extends BatteryStats {
// Distribute measured mobile radio charge consumption based on app radio
// active time
if (uidEstimatedConsumptionMah != null) {
- uidEstimatedConsumptionMah.add(u.getUid(),
+ uidEstimatedConsumptionMah.incrementValue(u.getUid(),
mMobileRadioPowerCalculator.calcPowerFromRadioActiveDurationMah(
appRadioTimeUs / 1000));
}
@@ -13303,7 +13362,6 @@ public class BatteryStatsImpl extends BatteryStats {
totalEstimatedConsumptionMah, elapsedRealtimeMs);
}
- mNetworkStatsPool.release(delta);
delta = null;
}
}
@@ -13349,8 +13407,8 @@ public class BatteryStatsImpl extends BatteryStats {
energy = info.getControllerEnergyUsed();
if (!info.getUidTraffic().isEmpty()) {
for (UidTraffic traffic : info.getUidTraffic()) {
- add(uidRxBytes, traffic.getUid(), traffic.getRxBytes());
- add(uidTxBytes, traffic.getUid(), traffic.getTxBytes());
+ uidRxBytes.incrementValue(traffic.getUid(), traffic.getRxBytes());
+ uidTxBytes.incrementValue(traffic.getUid(), traffic.getTxBytes());
}
}
}
@@ -13442,6 +13500,9 @@ public class BatteryStatsImpl extends BatteryStats {
long leftOverRxTimeMs = rxTimeMs;
long leftOverTxTimeMs = txTimeMs;
+ final SparseLongArray rxTimesMs = new SparseLongArray(uidCount);
+ final SparseLongArray txTimesMs = new SparseLongArray(uidCount);
+
for (int i = 0; i < uidCount; i++) {
final Uid u = mUidStats.valueAt(i);
if (u.mBluetoothScanTimer == null) {
@@ -13471,15 +13532,11 @@ public class BatteryStatsImpl extends BatteryStats {
scanTimeTxSinceMarkMs = (txTimeMs * scanTimeTxSinceMarkMs) / totalScanTimeMs;
}
- final ControllerActivityCounterImpl counter =
- u.getOrCreateBluetoothControllerActivityLocked();
- counter.getOrCreateRxTimeCounter()
- .increment(scanTimeRxSinceMarkMs, elapsedRealtimeMs);
- counter.getOrCreateTxTimeCounters()[0]
- .increment(scanTimeTxSinceMarkMs, elapsedRealtimeMs);
+ rxTimesMs.incrementValue(u.getUid(), scanTimeRxSinceMarkMs);
+ txTimesMs.incrementValue(u.getUid(), scanTimeTxSinceMarkMs);
if (uidEstimatedConsumptionMah != null) {
- uidEstimatedConsumptionMah.add(u.getUid(),
+ uidEstimatedConsumptionMah.incrementValue(u.getUid(),
mBluetoothPowerCalculator.calculatePowerMah(
scanTimeRxSinceMarkMs, scanTimeTxSinceMarkMs, 0));
}
@@ -13540,29 +13597,45 @@ public class BatteryStatsImpl extends BatteryStats {
if (totalRxBytes > 0 && rxBytes > 0) {
final long timeRxMs = (leftOverRxTimeMs * rxBytes) / totalRxBytes;
- if (DEBUG_ENERGY) {
- Slog.d(TAG, "UID=" + uid + " rx_bytes=" + rxBytes + " rx_time=" + timeRxMs);
- }
- counter.getOrCreateRxTimeCounter().increment(timeRxMs, elapsedRealtimeMs);
-
- if (uidEstimatedConsumptionMah != null) {
- uidEstimatedConsumptionMah.add(u.getUid(),
- mBluetoothPowerCalculator.calculatePowerMah(timeRxMs, 0, 0));
- }
+ rxTimesMs.incrementValue(uid, timeRxMs);
}
if (totalTxBytes > 0 && txBytes > 0) {
final long timeTxMs = (leftOverTxTimeMs * txBytes) / totalTxBytes;
- if (DEBUG_ENERGY) {
- Slog.d(TAG, "UID=" + uid + " tx_bytes=" + txBytes + " tx_time=" + timeTxMs);
- }
- counter.getOrCreateTxTimeCounters()[0]
- .increment(timeTxMs, elapsedRealtimeMs);
+ txTimesMs.incrementValue(uid, timeTxMs);
+ }
+ }
- if (uidEstimatedConsumptionMah != null) {
- uidEstimatedConsumptionMah.add(u.getUid(),
- mBluetoothPowerCalculator.calculatePowerMah(0, timeTxMs, 0));
- }
+ for (int i = 0; i < txTimesMs.size(); i++) {
+ final int uid = txTimesMs.keyAt(i);
+ final long myTxTimeMs = txTimesMs.valueAt(i);
+ if (DEBUG_ENERGY) {
+ Slog.d(TAG, " TxTime for UID " + uid + ": " + myTxTimeMs + " ms");
+ }
+ getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
+ .getOrCreateBluetoothControllerActivityLocked()
+ .getOrCreateTxTimeCounters()[0]
+ .increment(myTxTimeMs, elapsedRealtimeMs);
+ if (uidEstimatedConsumptionMah != null) {
+ uidEstimatedConsumptionMah.incrementValue(uid,
+ mBluetoothPowerCalculator.calculatePowerMah(0, myTxTimeMs, 0));
+ }
+ }
+
+ for (int i = 0; i < rxTimesMs.size(); i++) {
+ final int uid = rxTimesMs.keyAt(i);
+ final long myRxTimeMs = rxTimesMs.valueAt(i);
+ if (DEBUG_ENERGY) {
+ Slog.d(TAG, " RxTime for UID " + uid + ": " + myRxTimeMs + " ms");
+ }
+
+ getUidStatsLocked(rxTimesMs.keyAt(i), elapsedRealtimeMs, uptimeMs)
+ .getOrCreateBluetoothControllerActivityLocked()
+ .getOrCreateRxTimeCounter()
+ .increment(myRxTimeMs, elapsedRealtimeMs);
+ if (uidEstimatedConsumptionMah != null) {
+ uidEstimatedConsumptionMah.incrementValue(uid,
+ mBluetoothPowerCalculator.calculatePowerMah(myRxTimeMs, 0, 0));
}
}
}
@@ -16183,6 +16256,18 @@ public class BatteryStatsImpl extends BatteryStats {
iPw.decreaseIndent();
}
+ /**
+ * Dump Power Profile
+ */
+ @GuardedBy("this")
+ public void dumpPowerProfileLocked(PrintWriter pw) {
+ final IndentingPrintWriter iPw = new IndentingPrintWriter(pw, " ");
+ iPw.printf("Power Profile: \n");
+ iPw.increaseIndent();
+ mPowerProfile.dump(iPw);
+ iPw.decreaseIndent();
+ }
+
final ReentrantLock mWriteLock = new ReentrantLock();
@GuardedBy("this")
@@ -18140,8 +18225,4 @@ public class BatteryStatsImpl extends BatteryStats {
pw.println();
dumpMeasuredEnergyStatsLocked(pw);
}
-
- private static void add(SparseLongArray array, int key, long delta) {
- array.put(key, array.get(key) + delta);
- }
}
diff --git a/core/java/com/android/internal/os/BatteryUsageStatsStore.java b/core/java/com/android/internal/os/BatteryUsageStatsStore.java
index fd54b32bd12f..af82f40013d8 100644
--- a/core/java/com/android/internal/os/BatteryUsageStatsStore.java
+++ b/core/java/com/android/internal/os/BatteryUsageStatsStore.java
@@ -58,6 +58,7 @@ public class BatteryUsageStatsStore {
new BatteryUsageStatsQuery.Builder()
.setMaxStatsAgeMs(0)
.includePowerModels()
+ .includeProcessStateData()
.build());
private static final String BATTERY_USAGE_STATS_DIR = "battery-usage-stats";
private static final String SNAPSHOT_FILE_EXTENSION = ".bus";
diff --git a/core/java/com/android/internal/os/BinderLatencyObserver.java b/core/java/com/android/internal/os/BinderLatencyObserver.java
index 20cf102953e4..e9d55db3a5b4 100644
--- a/core/java/com/android/internal/os/BinderLatencyObserver.java
+++ b/core/java/com/android/internal/os/BinderLatencyObserver.java
@@ -19,7 +19,6 @@ package com.android.internal.os;
import android.annotation.Nullable;
import android.os.Binder;
import android.os.Handler;
-import android.os.Looper;
import android.os.SystemClock;
import android.util.ArrayMap;
import android.util.Slog;
@@ -181,7 +180,7 @@ public class BinderLatencyObserver {
}
public Handler getHandler() {
- return new Handler(Looper.getMainLooper());
+ return BackgroundThread.getHandler();
}
}
diff --git a/core/java/com/android/internal/os/BluetoothPowerCalculator.java b/core/java/com/android/internal/os/BluetoothPowerCalculator.java
index c322258eda85..20535d29afcd 100644
--- a/core/java/com/android/internal/os/BluetoothPowerCalculator.java
+++ b/core/java/com/android/internal/os/BluetoothPowerCalculator.java
@@ -15,6 +15,7 @@
*/
package com.android.internal.os;
+import android.annotation.Nullable;
import android.os.BatteryConsumer;
import android.os.BatteryStats;
import android.os.BatteryStats.ControllerActivityCounter;
@@ -26,19 +27,33 @@ import android.os.UserHandle;
import android.util.Log;
import android.util.SparseArray;
+import java.util.Arrays;
import java.util.List;
public class BluetoothPowerCalculator extends PowerCalculator {
private static final String TAG = "BluetoothPowerCalc";
private static final boolean DEBUG = BatteryStatsHelper.DEBUG;
+
+ private static final BatteryConsumer.Key[] UNINITIALIZED_KEYS = new BatteryConsumer.Key[0];
+
private final double mIdleMa;
private final double mRxMa;
private final double mTxMa;
private final boolean mHasBluetoothPowerController;
private static class PowerAndDuration {
+ // Return value of BT duration per app
public long durationMs;
+ // Return value of BT power per app
public double powerMah;
+
+ public BatteryConsumer.Key[] keys;
+ public double[] powerPerKeyMah;
+
+ // Aggregated BT duration across all apps
+ public long totalDurationMs;
+ // Aggregated BT power across all apps
+ public double totalPowerMah;
}
public BluetoothPowerCalculator(PowerProfile profile) {
@@ -55,59 +70,88 @@ public class BluetoothPowerCalculator extends PowerCalculator {
return;
}
- final PowerAndDuration total = new PowerAndDuration();
+ BatteryConsumer.Key[] keys = UNINITIALIZED_KEYS;
+ final PowerAndDuration powerAndDuration = new PowerAndDuration();
final SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders =
builder.getUidBatteryConsumerBuilders();
for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) {
final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i);
- calculateApp(app, total, query);
+ if (keys == UNINITIALIZED_KEYS) {
+ if (query.isProcessStateDataNeeded()) {
+ keys = app.getKeys(BatteryConsumer.POWER_COMPONENT_BLUETOOTH);
+ powerAndDuration.keys = keys;
+ powerAndDuration.powerPerKeyMah = new double[keys.length];
+ } else {
+ keys = null;
+ }
+ }
+ calculateApp(app, powerAndDuration, query);
}
final long measuredChargeUC = batteryStats.getBluetoothMeasuredBatteryConsumptionUC();
final int powerModel = getPowerModel(measuredChargeUC, query);
final ControllerActivityCounter activityCounter =
batteryStats.getBluetoothControllerActivity();
- final long systemDurationMs = calculateDuration(activityCounter);
- final double systemPowerMah = calculatePowerMah(powerModel, measuredChargeUC,
- activityCounter, query.shouldForceUsePowerProfileModel());
+ calculatePowerAndDuration(null, powerModel, measuredChargeUC,
+ activityCounter, query.shouldForceUsePowerProfileModel(), powerAndDuration);
// Subtract what the apps used, but clamp to 0.
- final long systemComponentDurationMs = Math.max(0, systemDurationMs - total.durationMs);
+ final long systemComponentDurationMs = Math.max(0,
+ powerAndDuration.durationMs - powerAndDuration.totalDurationMs);
if (DEBUG) {
Log.d(TAG, "Bluetooth active: time=" + (systemComponentDurationMs)
- + " power=" + formatCharge(systemPowerMah));
+ + " power=" + formatCharge(powerAndDuration.powerMah));
}
builder.getAggregateBatteryConsumerBuilder(
- BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
- .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_BLUETOOTH, systemDurationMs)
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
+ .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_BLUETOOTH,
+ powerAndDuration.durationMs)
.setConsumedPower(BatteryConsumer.POWER_COMPONENT_BLUETOOTH,
- Math.max(systemPowerMah, total.powerMah), powerModel);
+ Math.max(powerAndDuration.powerMah, powerAndDuration.totalPowerMah),
+ powerModel);
builder.getAggregateBatteryConsumerBuilder(
- BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS)
- .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_BLUETOOTH, total.durationMs)
- .setConsumedPower(BatteryConsumer.POWER_COMPONENT_BLUETOOTH, total.powerMah,
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS)
+ .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_BLUETOOTH,
+ powerAndDuration.totalDurationMs)
+ .setConsumedPower(BatteryConsumer.POWER_COMPONENT_BLUETOOTH,
+ powerAndDuration.totalPowerMah,
powerModel);
}
- private void calculateApp(UidBatteryConsumer.Builder app, PowerAndDuration total,
+ private void calculateApp(UidBatteryConsumer.Builder app, PowerAndDuration powerAndDuration,
BatteryUsageStatsQuery query) {
final long measuredChargeUC =
app.getBatteryStatsUid().getBluetoothMeasuredBatteryConsumptionUC();
final int powerModel = getPowerModel(measuredChargeUC, query);
final ControllerActivityCounter activityCounter =
app.getBatteryStatsUid().getBluetoothControllerActivity();
- final long durationMs = calculateDuration(activityCounter);
- final double powerMah = calculatePowerMah(powerModel, measuredChargeUC, activityCounter,
- query.shouldForceUsePowerProfileModel());
+ calculatePowerAndDuration(app.getBatteryStatsUid(), powerModel, measuredChargeUC,
+ activityCounter, query.shouldForceUsePowerProfileModel(), powerAndDuration);
- app.setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_BLUETOOTH, durationMs)
- .setConsumedPower(BatteryConsumer.POWER_COMPONENT_BLUETOOTH, powerMah, powerModel);
+ app.setUsageDurationMillis(
+ BatteryConsumer.POWER_COMPONENT_BLUETOOTH, powerAndDuration.durationMs)
+ .setConsumedPower(
+ BatteryConsumer.POWER_COMPONENT_BLUETOOTH, powerAndDuration.powerMah,
+ powerModel);
+
+ powerAndDuration.totalDurationMs += powerAndDuration.durationMs;
+ powerAndDuration.totalPowerMah += powerAndDuration.powerMah;
- total.durationMs += durationMs;
- total.powerMah += powerMah;
+ if (query.isProcessStateDataNeeded() && powerAndDuration.keys != null) {
+ for (int j = 0; j < powerAndDuration.keys.length; j++) {
+ BatteryConsumer.Key key = powerAndDuration.keys[j];
+ final int processState = key.processState;
+ if (processState == BatteryConsumer.PROCESS_STATE_UNSPECIFIED) {
+ // Already populated with the powerAndDuration across all process states
+ continue;
+ }
+
+ app.setConsumedPower(key, powerAndDuration.powerPerKeyMah[j], powerModel);
+ }
+ }
}
@Override
@@ -117,12 +161,12 @@ public class BluetoothPowerCalculator extends PowerCalculator {
return;
}
- PowerAndDuration total = new PowerAndDuration();
+ PowerAndDuration powerAndDuration = new PowerAndDuration();
for (int i = sippers.size() - 1; i >= 0; i--) {
final BatterySipper app = sippers.get(i);
if (app.drainType == BatterySipper.DrainType.APP) {
- calculateApp(app, app.uidObj, statsType, total);
+ calculateApp(app, app.uidObj, statsType, powerAndDuration);
}
}
@@ -131,13 +175,14 @@ public class BluetoothPowerCalculator extends PowerCalculator {
final int powerModel = getPowerModel(measuredChargeUC);
final ControllerActivityCounter activityCounter =
batteryStats.getBluetoothControllerActivity();
- final long systemDurationMs = calculateDuration(activityCounter);
- final double systemPowerMah =
- calculatePowerMah(powerModel, measuredChargeUC, activityCounter, false);
+ calculatePowerAndDuration(null, powerModel, measuredChargeUC, activityCounter, false,
+ powerAndDuration);
// Subtract what the apps used, but clamp to 0.
- final double powerMah = Math.max(0, systemPowerMah - total.powerMah);
- final long durationMs = Math.max(0, systemDurationMs - total.durationMs);
+ final double powerMah = Math.max(0,
+ powerAndDuration.powerMah - powerAndDuration.totalPowerMah);
+ final long durationMs = Math.max(0,
+ powerAndDuration.durationMs - powerAndDuration.totalDurationMs);
if (DEBUG && powerMah != 0) {
Log.d(TAG, "Bluetooth active: time=" + (durationMs)
+ " power=" + formatCharge(powerMah));
@@ -160,65 +205,102 @@ public class BluetoothPowerCalculator extends PowerCalculator {
}
private void calculateApp(BatterySipper app, BatteryStats.Uid u, int statsType,
- PowerAndDuration total) {
-
+ PowerAndDuration powerAndDuration) {
final long measuredChargeUC = u.getBluetoothMeasuredBatteryConsumptionUC();
final int powerModel = getPowerModel(measuredChargeUC);
final ControllerActivityCounter activityCounter = u.getBluetoothControllerActivity();
- final long durationMs = calculateDuration(activityCounter);
- final double powerMah = calculatePowerMah(powerModel, measuredChargeUC, activityCounter,
- false);
+ calculatePowerAndDuration(u, powerModel, measuredChargeUC, activityCounter,
+ false, powerAndDuration);
- app.bluetoothRunningTimeMs = durationMs;
- app.bluetoothPowerMah = powerMah;
+ app.bluetoothRunningTimeMs = powerAndDuration.durationMs;
+ app.bluetoothPowerMah = powerAndDuration.powerMah;
app.btRxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_BT_RX_DATA, statsType);
app.btTxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_BT_TX_DATA, statsType);
- total.durationMs += durationMs;
- total.powerMah += powerMah;
+ powerAndDuration.totalDurationMs += powerAndDuration.durationMs;
+ powerAndDuration.totalPowerMah += powerAndDuration.powerMah;
}
- private long calculateDuration(ControllerActivityCounter counter) {
+ /** Returns bluetooth power usage based on the best data available. */
+ private void calculatePowerAndDuration(@Nullable BatteryStats.Uid uid,
+ @BatteryConsumer.PowerModel int powerModel,
+ long measuredChargeUC, ControllerActivityCounter counter, boolean ignoreReportedPower,
+ PowerAndDuration powerAndDuration) {
if (counter == null) {
- return 0;
+ powerAndDuration.durationMs = 0;
+ powerAndDuration.powerMah = 0;
+ if (powerAndDuration.powerPerKeyMah != null) {
+ Arrays.fill(powerAndDuration.powerPerKeyMah, 0);
+ }
+ return;
}
- return counter.getIdleTimeCounter().getCountLocked(BatteryStats.STATS_SINCE_CHARGED)
- + counter.getRxTimeCounter().getCountLocked(BatteryStats.STATS_SINCE_CHARGED)
- + counter.getTxTimeCounters()[0].getCountLocked(BatteryStats.STATS_SINCE_CHARGED);
- }
-
- /** Returns bluetooth power usage based on the best data available. */
- private double calculatePowerMah(@BatteryConsumer.PowerModel int powerModel,
- long measuredChargeUC, ControllerActivityCounter counter, boolean ignoreReportedPower) {
- if (powerModel == BatteryConsumer.POWER_MODEL_MEASURED_ENERGY) {
- return uCtoMah(measuredChargeUC);
- }
+ final BatteryStats.LongCounter idleTimeCounter = counter.getIdleTimeCounter();
+ final BatteryStats.LongCounter rxTimeCounter = counter.getRxTimeCounter();
+ final BatteryStats.LongCounter txTimeCounter = counter.getTxTimeCounters()[0];
+ final long idleTimeMs = idleTimeCounter.getCountLocked(BatteryStats.STATS_SINCE_CHARGED);
+ final long rxTimeMs = rxTimeCounter.getCountLocked(BatteryStats.STATS_SINCE_CHARGED);
+ final long txTimeMs = txTimeCounter.getCountLocked(BatteryStats.STATS_SINCE_CHARGED);
- if (counter == null) {
- return 0;
- }
+ powerAndDuration.durationMs = idleTimeMs + rxTimeMs + txTimeMs;
- if (!ignoreReportedPower) {
- final double powerMah =
- counter.getPowerCounter().getCountLocked(BatteryStats.STATS_SINCE_CHARGED)
- / (double) (1000 * 60 * 60);
- if (powerMah != 0) {
- return powerMah;
+ if (powerModel == BatteryConsumer.POWER_MODEL_MEASURED_ENERGY) {
+ powerAndDuration.powerMah = uCtoMah(measuredChargeUC);
+ if (uid != null && powerAndDuration.keys != null) {
+ for (int i = 0; i < powerAndDuration.keys.length; i++) {
+ BatteryConsumer.Key key = powerAndDuration.keys[i];
+ final int processState = key.processState;
+ if (processState == BatteryConsumer.PROCESS_STATE_UNSPECIFIED) {
+ // Already populated with the powerAndDuration across all process states
+ continue;
+ }
+
+ powerAndDuration.powerPerKeyMah[i] =
+ uCtoMah(uid.getBluetoothMeasuredBatteryConsumptionUC(processState));
+ }
+ }
+ } else {
+ if (!ignoreReportedPower) {
+ final double powerMah =
+ counter.getPowerCounter().getCountLocked(BatteryStats.STATS_SINCE_CHARGED)
+ / (double) (1000 * 60 * 60);
+ if (powerMah != 0) {
+ powerAndDuration.powerMah = powerMah;
+ if (powerAndDuration.powerPerKeyMah != null) {
+ // Leave this use case unsupported: used energy is reported
+ // via BluetoothActivityEnergyInfo rather than PowerStats HAL.
+ Arrays.fill(powerAndDuration.powerPerKeyMah, 0);
+ }
+ return;
+ }
}
- }
- if (!mHasBluetoothPowerController) {
- return 0;
+ if (mHasBluetoothPowerController) {
+ powerAndDuration.powerMah = calculatePowerMah(rxTimeMs, txTimeMs, idleTimeMs);
+
+ if (powerAndDuration.keys != null) {
+ for (int i = 0; i < powerAndDuration.keys.length; i++) {
+ BatteryConsumer.Key key = powerAndDuration.keys[i];
+ final int processState = key.processState;
+ if (processState == BatteryConsumer.PROCESS_STATE_UNSPECIFIED) {
+ // Already populated with the powerAndDuration across all process states
+ continue;
+ }
+
+ powerAndDuration.powerPerKeyMah[i] =
+ calculatePowerMah(
+ rxTimeCounter.getCountForProcessState(processState),
+ txTimeCounter.getCountForProcessState(processState),
+ idleTimeCounter.getCountForProcessState(processState));
+ }
+ }
+ } else {
+ powerAndDuration.powerMah = 0;
+ if (powerAndDuration.powerPerKeyMah != null) {
+ Arrays.fill(powerAndDuration.powerPerKeyMah, 0);
+ }
+ }
}
-
- final long idleTimeMs =
- counter.getIdleTimeCounter().getCountLocked(BatteryStats.STATS_SINCE_CHARGED);
- final long rxTimeMs =
- counter.getRxTimeCounter().getCountLocked(BatteryStats.STATS_SINCE_CHARGED);
- final long txTimeMs =
- counter.getTxTimeCounters()[0].getCountLocked(BatteryStats.STATS_SINCE_CHARGED);
- return calculatePowerMah(rxTimeMs, txTimeMs, idleTimeMs);
}
/** Returns estimated bluetooth power usage based on usage times. */
diff --git a/core/java/com/android/internal/os/OWNERS b/core/java/com/android/internal/os/OWNERS
index 7766b77ab3c6..fd1d86b27834 100644
--- a/core/java/com/android/internal/os/OWNERS
+++ b/core/java/com/android/internal/os/OWNERS
@@ -12,4 +12,5 @@ per-file *PowerCalculator* = file:/BATTERY_STATS_OWNERS
per-file *PowerEstimator* = file:/BATTERY_STATS_OWNERS
per-file *Kernel* = file:/BATTERY_STATS_OWNERS
per-file *MultiState* = file:/BATTERY_STATS_OWNERS
+per-file *PowerProfile* = file:/BATTERY_STATS_OWNERS
diff --git a/core/java/com/android/internal/os/PowerProfile.java b/core/java/com/android/internal/os/PowerProfile.java
index 4d19b35b1e16..54e65e079b83 100644
--- a/core/java/com/android/internal/os/PowerProfile.java
+++ b/core/java/com/android/internal/os/PowerProfile.java
@@ -17,24 +17,31 @@
package com.android.internal.os;
+import android.annotation.LongDef;
import android.annotation.StringDef;
+import android.annotation.XmlRes;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
+import android.util.IndentingPrintWriter;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.power.ModemPowerProfile;
import com.android.internal.util.XmlUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
+import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
/**
@@ -259,6 +266,34 @@ public class PowerProfile {
public @interface PowerGroup {}
/**
+ * Constants for generating a 64bit power constant key.
+ *
+ * The bitfields of a key describes what its corresponding power constant represents:
+ * [63:40] - RESERVED
+ * [39:32] - {@link Subsystem} (max count = 16).
+ * [31:0] - per Subsystem fields, see {@link ModemPowerProfile}.
+ *
+ */
+ private static final long SUBSYSTEM_MASK = 0xF_0000_0000L;
+ /**
+ * Power constant not associated with a subsystem.
+ */
+ public static final long SUBSYSTEM_NONE = 0x0_0000_0000L;
+ /**
+ * Modem power constant.
+ */
+ public static final long SUBSYSTEM_MODEM = 0x1_0000_0000L;
+
+ @LongDef(prefix = { "SUBSYSTEM_" }, value = {
+ SUBSYSTEM_NONE,
+ SUBSYSTEM_MODEM,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Subsystem {}
+
+ private static final long SUBSYSTEM_FIELDS_MASK = 0xFFFF_FFFF;
+
+ /**
* A map from Power Use Item to its power consumption.
*/
static final HashMap<String, Double> sPowerItemMap = new HashMap<>();
@@ -268,12 +303,16 @@ public class PowerProfile {
*/
static final HashMap<String, Double[]> sPowerArrayMap = new HashMap<>();
+ static final ModemPowerProfile sModemPowerProfile = new ModemPowerProfile();
+
private static final String TAG_DEVICE = "device";
private static final String TAG_ITEM = "item";
private static final String TAG_ARRAY = "array";
private static final String TAG_ARRAYITEM = "value";
private static final String ATTR_NAME = "name";
+ private static final String TAG_MODEM = "modem";
+
private static final Object sLock = new Object();
@VisibleForTesting
@@ -289,19 +328,40 @@ public class PowerProfile {
public PowerProfile(Context context, boolean forTest) {
// Read the XML file for the given profile (normally only one per device)
synchronized (sLock) {
- if (sPowerItemMap.size() == 0 && sPowerArrayMap.size() == 0) {
- readPowerValuesFromXml(context, forTest);
- }
- initCpuClusters();
- initDisplays();
+ final int xmlId = forTest ? com.android.internal.R.xml.power_profile_test
+ : com.android.internal.R.xml.power_profile;
+ initLocked(context, xmlId);
+ }
+ }
+
+ /**
+ * Reinitialize the PowerProfile with the provided XML.
+ * WARNING: use only for testing!
+ */
+ @VisibleForTesting
+ public void forceInitForTesting(Context context, @XmlRes int xmlId) {
+ synchronized (sLock) {
+ sPowerItemMap.clear();
+ sPowerArrayMap.clear();
+ sModemPowerProfile.clear();
+ initLocked(context, xmlId);
+ }
+
+ }
+
+ @GuardedBy("sLock")
+ private void initLocked(Context context, @XmlRes int xmlId) {
+ if (sPowerItemMap.size() == 0 && sPowerArrayMap.size() == 0) {
+ readPowerValuesFromXml(context, xmlId);
}
+ initCpuClusters();
+ initDisplays();
+ initModem();
}
- private void readPowerValuesFromXml(Context context, boolean forTest) {
- final int id = forTest ? com.android.internal.R.xml.power_profile_test :
- com.android.internal.R.xml.power_profile;
+ private void readPowerValuesFromXml(Context context, @XmlRes int xmlId) {
final Resources resources = context.getResources();
- XmlResourceParser parser = resources.getXml(id);
+ XmlResourceParser parser = resources.getXml(xmlId);
boolean parsingArray = false;
ArrayList<Double> array = new ArrayList<>();
String arrayName = null;
@@ -340,6 +400,8 @@ public class PowerProfile {
array.add(value);
}
}
+ } else if (element.equals(TAG_MODEM)) {
+ sModemPowerProfile.parseFromXml(parser);
}
}
if (parsingArray) {
@@ -515,6 +577,39 @@ public class PowerProfile {
return mNumDisplays;
}
+ private void initModem() {
+ handleDeprecatedModemConstant(ModemPowerProfile.MODEM_DRAIN_TYPE_SLEEP,
+ POWER_MODEM_CONTROLLER_SLEEP, 0);
+ handleDeprecatedModemConstant(ModemPowerProfile.MODEM_DRAIN_TYPE_IDLE,
+ POWER_MODEM_CONTROLLER_IDLE, 0);
+ handleDeprecatedModemConstant(
+ ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_RX,
+ POWER_MODEM_CONTROLLER_RX, 0);
+ handleDeprecatedModemConstant(
+ ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+ | ModemPowerProfile.MODEM_TX_LEVEL_0, POWER_MODEM_CONTROLLER_TX, 0);
+ handleDeprecatedModemConstant(
+ ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+ | ModemPowerProfile.MODEM_TX_LEVEL_1, POWER_MODEM_CONTROLLER_TX, 1);
+ handleDeprecatedModemConstant(
+ ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+ | ModemPowerProfile.MODEM_TX_LEVEL_2, POWER_MODEM_CONTROLLER_TX, 2);
+ handleDeprecatedModemConstant(
+ ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+ | ModemPowerProfile.MODEM_TX_LEVEL_3, POWER_MODEM_CONTROLLER_TX, 3);
+ handleDeprecatedModemConstant(
+ ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+ | ModemPowerProfile.MODEM_TX_LEVEL_4, POWER_MODEM_CONTROLLER_TX, 4);
+ }
+
+ private void handleDeprecatedModemConstant(int key, String deprecatedKey, int level) {
+ final double drain = sModemPowerProfile.getAverageBatteryDrainMa(key);
+ if (!Double.isNaN(drain)) return; // Value already set, don't overwrite it.
+
+ final double deprecatedDrain = getAveragePower(deprecatedKey, level);
+ sModemPowerProfile.setPowerConstant(key, Double.toString(deprecatedDrain));
+ }
+
/**
* Returns the number of memory bandwidth buckets defined in power_profile.xml, or a
* default value if the subsystem has no recorded value.
@@ -560,6 +655,43 @@ public class PowerProfile {
}
/**
+ * Returns the average current in mA consumed by a subsystem's specified operation, or the given
+ * default value if the subsystem has no recorded value.
+ *
+ * @param key that describes a subsystem's battery draining operation
+ * The key is built from multiple constant, see {@link Subsystem} and
+ * {@link ModemPowerProfile}.
+ * @param defaultValue the value to return if the subsystem has no recorded value.
+ * @return the average current in milliAmps.
+ */
+ public double getAverageBatteryDrainOrDefaultMa(long key, double defaultValue) {
+ final long subsystemType = key & SUBSYSTEM_MASK;
+ final int subsystemFields = (int) (key & SUBSYSTEM_FIELDS_MASK);
+
+ final double value;
+ if (subsystemType == SUBSYSTEM_MODEM) {
+ value = sModemPowerProfile.getAverageBatteryDrainMa(subsystemFields);
+ } else {
+ value = Double.NaN;
+ }
+
+ if (Double.isNaN(value)) return defaultValue;
+ return value;
+ }
+
+ /**
+ * Returns the average current in mA consumed by a subsystem's specified operation.
+ *
+ * @param key that describes a subsystem's battery draining operation
+ * The key is built from multiple constant, see {@link Subsystem} and
+ * {@link ModemPowerProfile}.
+ * @return the average current in milliAmps.
+ */
+ public double getAverageBatteryDrainMa(long key) {
+ return getAverageBatteryDrainOrDefaultMa(key, 0);
+ }
+
+ /**
* Returns the average current in mA consumed by the subsystem for the given level.
*
* @param type the subsystem type
@@ -784,6 +916,25 @@ public class PowerProfile {
PowerProfileProto.BATTERY_CAPACITY);
}
+ /**
+ * Dump the PowerProfile values.
+ */
+ public void dump(PrintWriter pw) {
+ final IndentingPrintWriter ipw = new IndentingPrintWriter(pw);
+ sPowerItemMap.forEach((key, value) -> {
+ ipw.print(key, value);
+ ipw.println();
+ });
+ sPowerArrayMap.forEach((key, value) -> {
+ ipw.print(key, Arrays.toString(value));
+ ipw.println();
+ });
+ ipw.println("Modem values:");
+ ipw.increaseIndent();
+ sModemPowerProfile.dump(ipw);
+ ipw.decreaseIndent();
+ }
+
// Writes items in sPowerItemMap to proto if exists.
private void writePowerConstantToProto(ProtoOutputStream proto, String key, long fieldId) {
if (sPowerItemMap.containsKey(key)) {
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index ba7a0ef893d0..d7eeb7b8dec0 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -87,6 +87,7 @@ import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
+import android.view.OnBackInvokedDispatcher;
import android.view.PendingInsetsController;
import android.view.ThreadedRenderer;
import android.view.View;
@@ -108,6 +109,7 @@ import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.widget.FrameLayout;
import android.widget.PopupWindow;
+import android.window.WindowOnBackInvokedDispatcher;
import com.android.internal.R;
import com.android.internal.graphics.drawable.BackgroundBlurDrawable;
@@ -294,6 +296,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
return true;
};
private Consumer<Boolean> mCrossWindowBlurEnabledListener;
+ private final WindowOnBackInvokedDispatcher mOnBackInvokedDispatcher;
DecorView(Context context, int featureId, PhoneWindow window,
WindowManager.LayoutParams params) {
@@ -322,6 +325,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
initResizingPaints();
mLegacyNavigationBarBackgroundPaint.setColor(Color.BLACK);
+ mOnBackInvokedDispatcher = new WindowOnBackInvokedDispatcher();
}
void setBackgroundFallback(@Nullable Drawable fallbackDrawable) {
@@ -1869,6 +1873,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
}
mPendingInsetsController.detach();
+ mOnBackInvokedDispatcher.detachFromWindow();
}
@Override
@@ -1913,6 +1918,11 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
return mPendingInsetsController;
}
+ @Override
+ public WindowOnBackInvokedDispatcher provideWindowOnBackInvokedDispatcher() {
+ return mOnBackInvokedDispatcher;
+ }
+
private ActionMode createActionMode(
int type, ActionMode.Callback2 callback, View originatingView) {
switch (type) {
@@ -2367,6 +2377,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
}
}
}
+ mOnBackInvokedDispatcher.clear();
}
@Override
@@ -2648,6 +2659,15 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
}
}
+ /**
+ * Returns the {@link OnBackInvokedDispatcher} on the decor view.
+ */
+ @Override
+ @Nullable
+ public OnBackInvokedDispatcher getOnBackInvokedDispatcher() {
+ return mOnBackInvokedDispatcher;
+ }
+
@Override
public String toString() {
return "DecorView@" + Integer.toHexString(this.hashCode()) + "["
diff --git a/core/java/com/android/internal/power/ModemPowerProfile.java b/core/java/com/android/internal/power/ModemPowerProfile.java
new file mode 100644
index 000000000000..afea69a9c3f2
--- /dev/null
+++ b/core/java/com/android/internal/power/ModemPowerProfile.java
@@ -0,0 +1,475 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.power;
+
+import android.annotation.IntDef;
+import android.content.res.XmlResourceParser;
+import android.telephony.ModemActivityInfo;
+import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.SparseDoubleArray;
+
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+
+/**
+ * ModemPowerProfile for handling the modem element in the power_profile.xml
+ */
+public class ModemPowerProfile {
+ private static final String TAG = "ModemPowerProfile";
+
+ private static final String TAG_SLEEP = "sleep";
+ private static final String TAG_IDLE = "idle";
+ private static final String TAG_ACTIVE = "active";
+ private static final String TAG_RECEIVE = "receive";
+ private static final String TAG_TRANSMIT = "transmit";
+ private static final String ATTR_RAT = "rat";
+ private static final String ATTR_NR_FREQUENCY = "nrFrequency";
+ private static final String ATTR_LEVEL = "level";
+
+ /**
+ * A flattened list of the modem power constant extracted from the given XML parser.
+ *
+ * The bitfields of a key describes what its corresponding power constant represents:
+ * [31:28] - {@link ModemDrainType} (max count = 16).
+ * [27:24] - {@link ModemTxLevel} (only for {@link MODEM_DRAIN_TYPE_TX}) (max count = 16).
+ * [23:20] - {@link ModemRatType} (max count = 16).
+ * [19:16] - {@link ModemNrFrequencyRange} (only for {@link MODEM_RAT_TYPE_NR})
+ * (max count = 16).
+ * [15:0] - RESERVED
+ */
+ private final SparseDoubleArray mPowerConstants = new SparseDoubleArray();
+
+ private static final int MODEM_DRAIN_TYPE_MASK = 0xF000_0000;
+ private static final int MODEM_TX_LEVEL_MASK = 0x0F00_0000;
+ private static final int MODEM_RAT_TYPE_MASK = 0x00F0_0000;
+ private static final int MODEM_NR_FREQUENCY_RANGE_MASK = 0x000F_0000;
+
+ /**
+ * Corresponds to the overall modem battery drain while asleep.
+ */
+ public static final int MODEM_DRAIN_TYPE_SLEEP = 0x0000_0000;
+
+ /**
+ * Corresponds to the overall modem battery drain while idle.
+ */
+ public static final int MODEM_DRAIN_TYPE_IDLE = 0x1000_0000;
+
+ /**
+ * Corresponds to the modem battery drain while receiving data. A specific Rx battery drain
+ * power constant can be selected using a bitwise OR (|) with {@link ModemRatType} and
+ * {@link ModemNrFrequencyRange} (when applicable).
+ */
+ public static final int MODEM_DRAIN_TYPE_RX = 0x2000_0000;
+
+ /**
+ * Corresponds to the modem battery drain while receiving data.
+ * {@link ModemTxLevel} must be specified with this drain type.
+ * Specific Tx battery drain power constanta can be selected using a bitwise OR (|) with
+ * {@link ModemRatType} and {@link ModemNrFrequencyRange} (when applicable).
+ */
+ public static final int MODEM_DRAIN_TYPE_TX = 0x3000_0000;
+
+ @IntDef(prefix = {"MODEM_DRAIN_TYPE_"}, value = {
+ MODEM_DRAIN_TYPE_SLEEP,
+ MODEM_DRAIN_TYPE_IDLE,
+ MODEM_DRAIN_TYPE_RX,
+ MODEM_DRAIN_TYPE_TX,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ModemDrainType {
+ }
+
+
+ private static final SparseArray<String> MODEM_DRAIN_TYPE_NAMES = new SparseArray<>(4);
+ static {
+ MODEM_DRAIN_TYPE_NAMES.put(MODEM_DRAIN_TYPE_SLEEP, "SLEEP");
+ MODEM_DRAIN_TYPE_NAMES.put(MODEM_DRAIN_TYPE_IDLE, "IDLE");
+ MODEM_DRAIN_TYPE_NAMES.put(MODEM_DRAIN_TYPE_RX, "RX");
+ MODEM_DRAIN_TYPE_NAMES.put(MODEM_DRAIN_TYPE_TX, "TX");
+ }
+
+ /**
+ * Corresponds to {@link ModemActivityInfo#TX_POWER_LEVEL_0}.
+ */
+
+ public static final int MODEM_TX_LEVEL_0 = 0x0000_0000;
+
+ /**
+ * Corresponds to {@link ModemActivityInfo#TX_POWER_LEVEL_1}.
+ */
+
+ public static final int MODEM_TX_LEVEL_1 = 0x0100_0000;
+
+ /**
+ * Corresponds to {@link ModemActivityInfo#TX_POWER_LEVEL_2}.
+ */
+
+ public static final int MODEM_TX_LEVEL_2 = 0x0200_0000;
+
+ /**
+ * Corresponds to {@link ModemActivityInfo#TX_POWER_LEVEL_3}.
+ */
+
+ public static final int MODEM_TX_LEVEL_3 = 0x0300_0000;
+
+ /**
+ * Corresponds to {@link ModemActivityInfo#TX_POWER_LEVEL_4}.
+ */
+
+ public static final int MODEM_TX_LEVEL_4 = 0x0400_0000;
+
+ private static final int MODEM_TX_LEVEL_COUNT = 5;
+
+ @IntDef(prefix = {"MODEM_TX_LEVEL_"}, value = {
+ MODEM_TX_LEVEL_0,
+ MODEM_TX_LEVEL_1,
+ MODEM_TX_LEVEL_2,
+ MODEM_TX_LEVEL_3,
+ MODEM_TX_LEVEL_4,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ModemTxLevel {
+ }
+
+ private static final SparseArray<String> MODEM_TX_LEVEL_NAMES = new SparseArray<>(5);
+ static {
+ MODEM_DRAIN_TYPE_NAMES.put(MODEM_TX_LEVEL_0, "0");
+ MODEM_DRAIN_TYPE_NAMES.put(MODEM_TX_LEVEL_1, "1");
+ MODEM_DRAIN_TYPE_NAMES.put(MODEM_TX_LEVEL_2, "2");
+ MODEM_DRAIN_TYPE_NAMES.put(MODEM_TX_LEVEL_3, "3");
+ MODEM_DRAIN_TYPE_NAMES.put(MODEM_TX_LEVEL_4, "4");
+ }
+
+ private static final int[] MODEM_TX_LEVEL_MAP = new int[]{
+ MODEM_TX_LEVEL_0,
+ MODEM_TX_LEVEL_1,
+ MODEM_TX_LEVEL_2,
+ MODEM_TX_LEVEL_3,
+ MODEM_TX_LEVEL_4};
+
+ /**
+ * Fallback for any active modem usage that does not match specified Radio Access Technology
+ * (RAT) power constants.
+ */
+ public static final int MODEM_RAT_TYPE_DEFAULT = 0x0000_0000;
+
+ /**
+ * Corresponds to active modem usage on 4G {@link TelephonyManager#NETWORK_TYPE_LTE} RAT.
+ */
+ public static final int MODEM_RAT_TYPE_LTE = 0x0010_0000;
+
+ /**
+ * Corresponds to active modem usage on 5G {@link TelephonyManager#NETWORK_TYPE_NR} RAT.
+ */
+ public static final int MODEM_RAT_TYPE_NR = 0x0020_0000;
+
+ @IntDef(prefix = {"MODEM_RAT_TYPE_"}, value = {
+ MODEM_RAT_TYPE_DEFAULT,
+ MODEM_RAT_TYPE_LTE,
+ MODEM_RAT_TYPE_NR,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ModemRatType {
+ }
+
+ private static final SparseArray<String> MODEM_RAT_TYPE_NAMES = new SparseArray<>(3);
+ static {
+ MODEM_RAT_TYPE_NAMES.put(MODEM_RAT_TYPE_DEFAULT, "DEFAULT");
+ MODEM_RAT_TYPE_NAMES.put(MODEM_RAT_TYPE_LTE, "LTE");
+ MODEM_RAT_TYPE_NAMES.put(MODEM_RAT_TYPE_NR, "NR");
+ }
+
+ /**
+ * Fallback for any active 5G modem usage that does not match specified NR frequency power
+ * constants.
+ */
+ public static final int MODEM_NR_FREQUENCY_RANGE_DEFAULT = 0x0000_0000;
+
+ /**
+ * Corresponds to active NR modem usage on {@link ServiceState#FREQUENCY_RANGE_LOW}.
+ */
+ public static final int MODEM_NR_FREQUENCY_RANGE_LOW = 0x0001_0000;
+
+ /**
+ * Corresponds to active NR modem usage on {@link ServiceState#FREQUENCY_RANGE_MID}.
+ */
+ public static final int MODEM_NR_FREQUENCY_RANGE_MID = 0x0002_0000;
+
+ /**
+ * Corresponds to active NR modem usage on {@link ServiceState#FREQUENCY_RANGE_HIGH}.
+ */
+ public static final int MODEM_NR_FREQUENCY_RANGE_HIGH = 0x0003_0000;
+
+ /**
+ * Corresponds to active NR modem usage on {@link ServiceState#FREQUENCY_RANGE_MMWAVE}.
+ */
+ public static final int MODEM_NR_FREQUENCY_RANGE_MMWAVE = 0x0004_0000;
+
+ @IntDef(prefix = {"MODEM_NR_FREQUENCY_RANGE_"}, value = {
+ MODEM_RAT_TYPE_DEFAULT,
+ MODEM_NR_FREQUENCY_RANGE_LOW,
+ MODEM_NR_FREQUENCY_RANGE_MID,
+ MODEM_NR_FREQUENCY_RANGE_HIGH,
+ MODEM_NR_FREQUENCY_RANGE_MMWAVE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ModemNrFrequencyRange {
+ }
+ private static final SparseArray<String> MODEM_NR_FREQUENCY_RANGE_NAMES = new SparseArray<>(5);
+ static {
+ MODEM_NR_FREQUENCY_RANGE_NAMES.put(MODEM_NR_FREQUENCY_RANGE_DEFAULT, "DEFAULT");
+ MODEM_NR_FREQUENCY_RANGE_NAMES.put(MODEM_NR_FREQUENCY_RANGE_LOW, "LOW");
+ MODEM_NR_FREQUENCY_RANGE_NAMES.put(MODEM_NR_FREQUENCY_RANGE_MID, "MID");
+ MODEM_NR_FREQUENCY_RANGE_NAMES.put(MODEM_NR_FREQUENCY_RANGE_HIGH, "HIGH");
+ MODEM_NR_FREQUENCY_RANGE_NAMES.put(MODEM_NR_FREQUENCY_RANGE_MMWAVE, "MMWAVE");
+ }
+
+ public ModemPowerProfile() {
+ }
+
+ /**
+ * Generates a ModemPowerProfile object from the <modem /> element of a power_profile.xml
+ */
+ public void parseFromXml(XmlResourceParser parser) throws IOException,
+ XmlPullParserException {
+ final int depth = parser.getDepth();
+ while (XmlUtils.nextElementWithin(parser, depth)) {
+ final String name = parser.getName();
+ switch (name) {
+ case TAG_SLEEP:
+ if (parser.next() != XmlPullParser.TEXT) {
+ continue;
+ }
+ final String sleepDrain = parser.getText();
+ setPowerConstant(MODEM_DRAIN_TYPE_SLEEP, sleepDrain);
+ break;
+ case TAG_IDLE:
+ if (parser.next() != XmlPullParser.TEXT) {
+ continue;
+ }
+ final String idleDrain = parser.getText();
+ setPowerConstant(MODEM_DRAIN_TYPE_IDLE, idleDrain);
+ break;
+ case TAG_ACTIVE:
+ parseActivePowerConstantsFromXml(parser);
+ break;
+ default:
+ Slog.e(TAG, "Unexpected element parsed: " + name);
+ }
+ }
+ }
+
+ /** Parse the <active /> XML element */
+ private void parseActivePowerConstantsFromXml(XmlResourceParser parser)
+ throws IOException, XmlPullParserException {
+ // Parse attributes to get the type of active modem usage the power constants are for.
+ final int ratType;
+ final int nrfType;
+ try {
+ ratType = getTypeFromAttribute(parser, ATTR_RAT, MODEM_RAT_TYPE_NAMES);
+ if (ratType == MODEM_RAT_TYPE_NR) {
+ nrfType = getTypeFromAttribute(parser, ATTR_NR_FREQUENCY,
+ MODEM_NR_FREQUENCY_RANGE_NAMES);
+ } else {
+ nrfType = 0;
+ }
+ } catch (IllegalArgumentException iae) {
+ Slog.e(TAG, "Failed parse to active modem power constants", iae);
+ return;
+ }
+
+ // Parse and populate the active modem use power constants.
+ final int depth = parser.getDepth();
+ while (XmlUtils.nextElementWithin(parser, depth)) {
+ final String name = parser.getName();
+ switch (name) {
+ case TAG_RECEIVE:
+ if (parser.next() != XmlPullParser.TEXT) {
+ continue;
+ }
+ final String rxDrain = parser.getText();
+ final int rxKey = MODEM_DRAIN_TYPE_RX | ratType | nrfType;
+ setPowerConstant(rxKey, rxDrain);
+ break;
+ case TAG_TRANSMIT:
+ final int level = XmlUtils.readIntAttribute(parser, ATTR_LEVEL, -1);
+ if (parser.next() != XmlPullParser.TEXT) {
+ continue;
+ }
+ final String txDrain = parser.getText();
+ if (level < 0 || level >= MODEM_TX_LEVEL_COUNT) {
+ Slog.e(TAG,
+ "Unexpected tx level: " + level + ". Must be between 0 and " + (
+ MODEM_TX_LEVEL_COUNT - 1));
+ continue;
+ }
+ final int modemTxLevel = MODEM_TX_LEVEL_MAP[level];
+ final int txKey = MODEM_DRAIN_TYPE_TX | modemTxLevel | ratType | nrfType;
+ setPowerConstant(txKey, txDrain);
+ break;
+ default:
+ Slog.e(TAG, "Unexpected element parsed: " + name);
+ }
+ }
+ }
+
+ private static int getTypeFromAttribute(XmlResourceParser parser, String attr,
+ SparseArray<String> names) {
+ final String value = XmlUtils.readStringAttribute(parser, attr);
+ if (value == null) {
+ // Attribute was not specified, just use the default.
+ return 0;
+ }
+ int index = -1;
+ final int size = names.size();
+ // Manual linear search for string. (SparseArray uses == not equals.)
+ for (int i = 0; i < size; i++) {
+ if (value.equals(names.valueAt(i))) {
+ index = i;
+ }
+ }
+ if (index < 0) {
+ final String[] stringNames = new String[size];
+ for (int i = 0; i < size; i++) {
+ stringNames[i] = names.valueAt(i);
+ }
+ throw new IllegalArgumentException(
+ "Unexpected " + attr + " value : " + value + ". Acceptable values are "
+ + Arrays.toString(stringNames));
+ }
+ return names.keyAt(index);
+ }
+
+ /**
+ * Set the average battery drain in milli-amps of the modem for a given drain type.
+ *
+ * @param key a key built from the union of {@link ModemDrainType}, {@link ModemTxLevel},
+ * {@link ModemRatType}, and {@link ModemNrFrequencyRange}.key
+ * @param value the battery dram in milli-amps for the given key.
+ */
+ public void setPowerConstant(int key, String value) {
+ try {
+ mPowerConstants.put(key, Double.valueOf(value));
+ } catch (Exception e) {
+ Slog.e(TAG, "Failed to set power constant 0x" + Integer.toHexString(
+ key) + "(" + keyToString(key) + ") to " + value, e);
+ }
+ }
+
+ /**
+ * Returns the average battery drain in milli-amps of the modem for a given drain type.
+ * Returns {@link Double.NaN} if a suitable value is not found for the given key.
+ *
+ * @param key a key built from the union of {@link ModemDrainType}, {@link ModemTxLevel},
+ * {@link ModemRatType}, and {@link ModemNrFrequencyRange}.
+ */
+ public double getAverageBatteryDrainMa(int key) {
+ int bestKey = key;
+ double value;
+ value = mPowerConstants.get(bestKey, Double.NaN);
+ if (!Double.isNaN(value)) return value;
+ // The power constant for given key was not explicitly set. Try to fallback to possible
+ // defaults.
+
+ if ((bestKey & MODEM_NR_FREQUENCY_RANGE_MASK) != MODEM_NR_FREQUENCY_RANGE_DEFAULT) {
+ // Fallback to NR Frequency default value
+ bestKey &= ~MODEM_NR_FREQUENCY_RANGE_MASK;
+ bestKey |= MODEM_NR_FREQUENCY_RANGE_DEFAULT;
+ value = mPowerConstants.get(bestKey, Double.NaN);
+ if (!Double.isNaN(value)) return value;
+ }
+
+ if ((bestKey & MODEM_RAT_TYPE_MASK) != MODEM_RAT_TYPE_DEFAULT) {
+ // Fallback to RAT default value
+ bestKey &= ~MODEM_RAT_TYPE_MASK;
+ bestKey |= MODEM_RAT_TYPE_DEFAULT;
+ value = mPowerConstants.get(bestKey, Double.NaN);
+ if (!Double.isNaN(value)) return value;
+ }
+
+ Slog.w(TAG,
+ "getAverageBatteryDrainMaH called with unexpected key: 0x" + Integer.toHexString(
+ key) + ", " + keyToString(key));
+ return Double.NaN;
+ }
+
+ private static String keyToString(int key) {
+ StringBuilder sb = new StringBuilder();
+ final int drainType = key & MODEM_DRAIN_TYPE_MASK;
+ appendFieldToString(sb, "drain", MODEM_DRAIN_TYPE_NAMES, drainType);
+ sb.append(",");
+
+ if (drainType == MODEM_DRAIN_TYPE_TX) {
+ final int txLevel = key & MODEM_TX_LEVEL_MASK;
+ appendFieldToString(sb, "level", MODEM_TX_LEVEL_NAMES, txLevel);
+ }
+
+ final int ratType = key & MODEM_RAT_TYPE_MASK;
+ appendFieldToString(sb, "RAT", MODEM_RAT_TYPE_NAMES, ratType);
+
+ if (ratType == MODEM_RAT_TYPE_NR) {
+ sb.append(",");
+ final int nrFreq = key & MODEM_NR_FREQUENCY_RANGE_MASK;
+ appendFieldToString(sb, "nrFreq", MODEM_NR_FREQUENCY_RANGE_NAMES, nrFreq);
+ }
+ return sb.toString();
+ }
+ private static void appendFieldToString(StringBuilder sb, String fieldName,
+ SparseArray<String> names, int key) {
+ sb.append(fieldName);
+ sb.append(":");
+ final String name = names.get(key, null);
+ if (name == null) {
+ sb.append("UNKNOWN(0x");
+ sb.append(Integer.toHexString(key));
+ sb.append(")");
+ } else {
+ sb.append(name);
+ }
+ }
+
+ /**
+ * Clear this ModemPowerProfile power constants.
+ */
+ public void clear() {
+ mPowerConstants.clear();
+ }
+
+
+ /**
+ * Dump this ModemPowerProfile power constants.
+ */
+ public void dump(PrintWriter pw) {
+ final int size = mPowerConstants.size();
+ for (int i = 0; i < size; i++) {
+ pw.print(keyToString(mPowerConstants.keyAt(i)));
+ pw.print("=");
+ pw.println(mPowerConstants.valueAt(i));
+ }
+ }
+}
diff --git a/core/java/com/android/internal/protolog/ProtoLogGroup.java b/core/java/com/android/internal/protolog/ProtoLogGroup.java
index 5ac493637822..def598ca8724 100644
--- a/core/java/com/android/internal/protolog/ProtoLogGroup.java
+++ b/core/java/com/android/internal/protolog/ProtoLogGroup.java
@@ -85,6 +85,8 @@ public enum ProtoLogGroup implements IProtoLogGroup {
WM_DEBUG_LAYER_MIRRORING(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true,
Consts.TAG_WM),
WM_DEBUG_WALLPAPER(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false, Consts.TAG_WM),
+ WM_DEBUG_BACK_PREVIEW(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true,
+ "CoreBackPreview"),
TEST_GROUP(true, true, false, "WindowManagerProtoLogTest");
private final boolean mEnabled;
diff --git a/core/java/com/android/internal/statusbar/StatusBarIcon.java b/core/java/com/android/internal/statusbar/StatusBarIcon.java
index 1d626235c4d2..4f80afaab696 100644
--- a/core/java/com/android/internal/statusbar/StatusBarIcon.java
+++ b/core/java/com/android/internal/statusbar/StatusBarIcon.java
@@ -81,9 +81,9 @@ public class StatusBarIcon implements Parcelable {
}
public void readFromParcel(Parcel in) {
- this.icon = (Icon) in.readParcelable(null);
+ this.icon = (Icon) in.readParcelable(null, android.graphics.drawable.Icon.class);
this.pkg = in.readString();
- this.user = (UserHandle) in.readParcelable(null);
+ this.user = (UserHandle) in.readParcelable(null, android.os.UserHandle.class);
this.iconLevel = in.readInt();
this.visible = in.readInt() != 0;
this.number = in.readInt();
diff --git a/core/java/com/android/internal/usb/DumpUtils.java b/core/java/com/android/internal/usb/DumpUtils.java
index 32601368f2fb..b32a6b078884 100644
--- a/core/java/com/android/internal/usb/DumpUtils.java
+++ b/core/java/com/android/internal/usb/DumpUtils.java
@@ -244,7 +244,12 @@ public class DumpUtils {
writeContaminantPresenceStatus(dump, "contaminant_presence_status",
UsbPortStatusProto.CONTAMINANT_PRESENCE_STATUS,
status.getContaminantDetectionStatus());
-
+ dump.write("usb_data_status", UsbPortStatusProto.USB_DATA_STATUS,
+ UsbPort.usbDataStatusToString(status.getUsbDataStatus()));
+ dump.write("is_power_transfer_limited", UsbPortStatusProto.IS_POWER_TRANSFER_LIMITED,
+ status.isPowerTransferLimited());
+ dump.write("usb_power_brick_status", UsbPortStatusProto.USB_POWER_BRICK_STATUS,
+ UsbPort.powerBrickStatusToString(status.getPowerBrickStatus()));
dump.end(token);
}
}
diff --git a/core/java/com/android/internal/util/FileRotator.java b/core/java/com/android/internal/util/FileRotator.java
index 3ca33203f554..4b3af1536175 100644
--- a/core/java/com/android/internal/util/FileRotator.java
+++ b/core/java/com/android/internal/util/FileRotator.java
@@ -17,7 +17,7 @@
package com.android.internal.util;
import android.os.FileUtils;
-import android.util.Slog;
+import android.util.Log;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
@@ -32,7 +32,6 @@ import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import libcore.io.IoUtils;
-import libcore.io.Streams;
/**
* Utility that rotates files over time, similar to {@code logrotate}. There is
@@ -47,6 +46,8 @@ import libcore.io.Streams;
* <p>
* Users must periodically call {@link #maybeRotate(long)} to perform actual
* rotation. Not inherently thread safe.
+ *
+ * @hide
*/
public class FileRotator {
private static final String TAG = "FileRotator";
@@ -110,7 +111,7 @@ public class FileRotator {
if (!name.startsWith(mPrefix)) continue;
if (name.endsWith(SUFFIX_BACKUP)) {
- if (LOGD) Slog.d(TAG, "recovering " + name);
+ if (LOGD) Log.d(TAG, "recovering " + name);
final File backupFile = new File(mBasePath, name);
final File file = new File(
@@ -120,7 +121,7 @@ public class FileRotator {
backupFile.renameTo(file);
} else if (name.endsWith(SUFFIX_NO_BACKUP)) {
- if (LOGD) Slog.d(TAG, "recovering " + name);
+ if (LOGD) Log.d(TAG, "recovering " + name);
final File noBackupFile = new File(mBasePath, name);
final File file = new File(
@@ -231,7 +232,7 @@ public class FileRotator {
* if the write fails.
*/
private void rewriteSingle(Rewriter rewriter, String name) throws IOException {
- if (LOGD) Slog.d(TAG, "rewriting " + name);
+ if (LOGD) Log.d(TAG, "rewriting " + name);
final File file = new File(mBasePath, name);
final File backupFile;
@@ -291,7 +292,7 @@ public class FileRotator {
// read file when it overlaps
if (info.startMillis <= matchEndMillis && matchStartMillis <= info.endMillis) {
- if (LOGD) Slog.d(TAG, "reading matching " + name);
+ if (LOGD) Log.d(TAG, "reading matching " + name);
final File file = new File(mBasePath, name);
readFile(file, reader);
@@ -348,7 +349,7 @@ public class FileRotator {
if (info.isActive()) {
if (info.startMillis <= rotateBefore) {
// found active file; rotate if old enough
- if (LOGD) Slog.d(TAG, "rotating " + name);
+ if (LOGD) Log.d(TAG, "rotating " + name);
info.endMillis = currentTimeMillis;
@@ -358,7 +359,7 @@ public class FileRotator {
}
} else if (info.endMillis <= deleteBefore) {
// found rotated file; delete if old enough
- if (LOGD) Slog.d(TAG, "deleting " + name);
+ if (LOGD) Log.d(TAG, "deleting " + name);
final File file = new File(mBasePath, name);
file.delete();
@@ -383,7 +384,10 @@ public class FileRotator {
writer.write(bos);
bos.flush();
} finally {
- FileUtils.sync(fos);
+ try {
+ fos.getFD().sync();
+ } catch (IOException e) {
+ }
IoUtils.closeQuietly(bos);
}
}
diff --git a/core/java/com/android/internal/util/ScreenshotHelper.java b/core/java/com/android/internal/util/ScreenshotHelper.java
index f46223ac8769..d3c3917cd791 100644
--- a/core/java/com/android/internal/util/ScreenshotHelper.java
+++ b/core/java/com/android/internal/util/ScreenshotHelper.java
@@ -71,11 +71,11 @@ public class ScreenshotHelper {
if (in.readInt() == 1) {
mBitmapBundle = in.readBundle(getClass().getClassLoader());
- mBoundsInScreen = in.readParcelable(Rect.class.getClassLoader());
- mInsets = in.readParcelable(Insets.class.getClassLoader());
+ mBoundsInScreen = in.readParcelable(Rect.class.getClassLoader(), android.graphics.Rect.class);
+ mInsets = in.readParcelable(Insets.class.getClassLoader(), android.graphics.Insets.class);
mTaskId = in.readInt();
mUserId = in.readInt();
- mTopComponent = in.readParcelable(ComponentName.class.getClassLoader());
+ mTopComponent = in.readParcelable(ComponentName.class.getClassLoader(), android.content.ComponentName.class);
}
}
diff --git a/core/java/com/android/internal/view/IInputMethod.aidl b/core/java/com/android/internal/view/IInputMethod.aidl
index 402fa64036a0..6a626ee6eb8f 100644
--- a/core/java/com/android/internal/view/IInputMethod.aidl
+++ b/core/java/com/android/internal/view/IInputMethod.aidl
@@ -53,8 +53,6 @@ oneway interface IInputMethod {
void setSessionEnabled(IInputMethodSession session, boolean enabled);
- void revokeSession(IInputMethodSession session);
-
void showSoftInput(in IBinder showInputToken, int flags, in ResultReceiver resultReceiver);
void hideSoftInput(in IBinder hideInputToken, int flags, in ResultReceiver resultReceiver);
diff --git a/core/java/com/android/internal/view/RootViewSurfaceTaker.java b/core/java/com/android/internal/view/RootViewSurfaceTaker.java
index efd5fb2e1edf..4b89bf5082ba 100644
--- a/core/java/com/android/internal/view/RootViewSurfaceTaker.java
+++ b/core/java/com/android/internal/view/RootViewSurfaceTaker.java
@@ -15,11 +15,12 @@
*/
package com.android.internal.view;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.view.InputQueue;
import android.view.PendingInsetsController;
import android.view.SurfaceHolder;
-import android.view.WindowInsetsController;
+import android.window.WindowOnBackInvokedDispatcher;
/** hahahah */
public interface RootViewSurfaceTaker {
@@ -30,4 +31,6 @@ public interface RootViewSurfaceTaker {
InputQueue.Callback willYouTakeTheInputQueue();
void onRootViewScrollYChanged(int scrollY);
@Nullable PendingInsetsController providePendingInsetsController();
+ /** @hide */
+ @NonNull WindowOnBackInvokedDispatcher provideWindowOnBackInvokedDispatcher();
}
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index f91776eaf259..f8ccde4cf4d9 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -1028,15 +1028,6 @@ public class LockPatternUtils {
}
/**
- * @return Whether tactile feedback for the pattern is enabled.
- */
- @UnsupportedAppUsage
- public boolean isTactileFeedbackEnabled() {
- return Settings.System.getIntForUser(mContentResolver,
- Settings.System.HAPTIC_FEEDBACK_ENABLED, 1, UserHandle.USER_CURRENT) != 0;
- }
-
- /**
* Set and store the lockout deadline, meaning the user can't attempt their unlock
* pattern until the deadline has passed.
* @return the chosen deadline.
diff --git a/core/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java
index 3994fbdca2df..2b6b933c6886 100644
--- a/core/java/com/android/internal/widget/LockPatternView.java
+++ b/core/java/com/android/internal/widget/LockPatternView.java
@@ -139,7 +139,6 @@ public class LockPatternView extends View {
private boolean mInputEnabled = true;
@UnsupportedAppUsage
private boolean mInStealthMode = false;
- private boolean mEnableHapticFeedback = true;
@UnsupportedAppUsage
private boolean mPatternInProgress = false;
private boolean mFadePattern = true;
@@ -401,13 +400,6 @@ public class LockPatternView extends View {
}
/**
- * @return Whether the view has tactile feedback enabled.
- */
- public boolean isTactileFeedbackEnabled() {
- return mEnableHapticFeedback;
- }
-
- /**
* Set whether the view is in stealth mode. If true, there will be no
* visible feedback as the user enters the pattern.
*
@@ -427,17 +419,6 @@ public class LockPatternView extends View {
}
/**
- * Set whether the view will use tactile feedback. If true, there will be
- * tactile feedback as the user enters the pattern.
- *
- * @param tactileFeedbackEnabled Whether tactile feedback is enabled
- */
- @UnsupportedAppUsage
- public void setTactileFeedbackEnabled(boolean tactileFeedbackEnabled) {
- mEnableHapticFeedback = tactileFeedbackEnabled;
- }
-
- /**
* Set the call back for pattern detection.
* @param onPatternListener The call back.
*/
@@ -783,11 +764,9 @@ public class LockPatternView extends View {
addCellToPattern(fillInGapCell);
}
addCellToPattern(cell);
- if (mEnableHapticFeedback) {
- performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
- HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING
- | HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
- }
+ performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
+ HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING
+ | HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
return cell;
}
return null;
@@ -1462,7 +1441,7 @@ public class LockPatternView extends View {
return new SavedState(superState,
patternString,
mPatternDisplayMode.ordinal(),
- mInputEnabled, mInStealthMode, mEnableHapticFeedback);
+ mInputEnabled, mInStealthMode);
}
@Override
@@ -1475,7 +1454,6 @@ public class LockPatternView extends View {
mPatternDisplayMode = DisplayMode.values()[ss.getDisplayMode()];
mInputEnabled = ss.isInputEnabled();
mInStealthMode = ss.isInStealthMode();
- mEnableHapticFeedback = ss.isTactileFeedbackEnabled();
}
/**
@@ -1487,20 +1465,18 @@ public class LockPatternView extends View {
private final int mDisplayMode;
private final boolean mInputEnabled;
private final boolean mInStealthMode;
- private final boolean mTactileFeedbackEnabled;
/**
* Constructor called from {@link LockPatternView#onSaveInstanceState()}
*/
@UnsupportedAppUsage
private SavedState(Parcelable superState, String serializedPattern, int displayMode,
- boolean inputEnabled, boolean inStealthMode, boolean tactileFeedbackEnabled) {
+ boolean inputEnabled, boolean inStealthMode) {
super(superState);
mSerializedPattern = serializedPattern;
mDisplayMode = displayMode;
mInputEnabled = inputEnabled;
mInStealthMode = inStealthMode;
- mTactileFeedbackEnabled = tactileFeedbackEnabled;
}
/**
@@ -1513,7 +1489,6 @@ public class LockPatternView extends View {
mDisplayMode = in.readInt();
mInputEnabled = (Boolean) in.readValue(null);
mInStealthMode = (Boolean) in.readValue(null);
- mTactileFeedbackEnabled = (Boolean) in.readValue(null);
}
public String getSerializedPattern() {
@@ -1532,10 +1507,6 @@ public class LockPatternView extends View {
return mInStealthMode;
}
- public boolean isTactileFeedbackEnabled(){
- return mTactileFeedbackEnabled;
- }
-
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
@@ -1543,7 +1514,6 @@ public class LockPatternView extends View {
dest.writeInt(mDisplayMode);
dest.writeValue(mInputEnabled);
dest.writeValue(mInStealthMode);
- dest.writeValue(mTactileFeedbackEnabled);
}
@SuppressWarnings({ "unused", "hiding" }) // Found using reflection
diff --git a/core/java/com/android/internal/widget/SlidingTab.java b/core/java/com/android/internal/widget/SlidingTab.java
index 5e6f3a46de7d..11566d9a026d 100644
--- a/core/java/com/android/internal/widget/SlidingTab.java
+++ b/core/java/com/android/internal/widget/SlidingTab.java
@@ -22,10 +22,9 @@ import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
-import android.media.AudioAttributes;
-import android.os.UserHandle;
+import android.os.VibrationAttributes;
+import android.os.VibrationEffect;
import android.os.Vibrator;
-import android.provider.Settings;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
@@ -68,10 +67,8 @@ public class SlidingTab extends ViewGroup {
private boolean mHoldLeftOnTransition = true;
private boolean mHoldRightOnTransition = true;
- private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
- .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
- .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
- .build();
+ private static final VibrationAttributes TOUCH_VIBRATION_ATTRIBUTES =
+ VibrationAttributes.createForUsage(VibrationAttributes.USAGE_TOUCH);
private OnTriggerListener mOnTriggerListener;
private int mGrabbedState = OnTriggerListener.NO_HANDLE;
@@ -834,16 +831,12 @@ public class SlidingTab extends ViewGroup {
* Triggers haptic feedback.
*/
private synchronized void vibrate(long duration) {
- final boolean hapticEnabled = Settings.System.getIntForUser(
- mContext.getContentResolver(), Settings.System.HAPTIC_FEEDBACK_ENABLED, 1,
- UserHandle.USER_CURRENT) != 0;
- if (hapticEnabled) {
- if (mVibrator == null) {
- mVibrator = (android.os.Vibrator) getContext()
- .getSystemService(Context.VIBRATOR_SERVICE);
- }
- mVibrator.vibrate(duration, VIBRATION_ATTRIBUTES);
+ if (mVibrator == null) {
+ mVibrator = getContext().getSystemService(Vibrator.class);
}
+ mVibrator.vibrate(
+ VibrationEffect.createOneShot(duration, VibrationEffect.DEFAULT_AMPLITUDE),
+ TOUCH_VIBRATION_ATTRIBUTES);
}
/**
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index a3ac472bb900..430d84ebb3e1 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -124,7 +124,6 @@ cc_library_shared {
"android_view_PointerIcon.cpp",
"android_view_Surface.cpp",
"android_view_SurfaceControl.cpp",
- "android_view_SurfaceControlFpsListener.cpp",
"android_view_SurfaceControlHdrLayerInfoListener.cpp",
"android_graphics_BLASTBufferQueue.cpp",
"android_view_SurfaceSession.cpp",
@@ -255,7 +254,6 @@ cc_library_shared {
"libandroidicu",
"libbattery",
"libbpf_android",
- "libnetdbpf",
"libnetdutils",
"libmemtrack",
"libandroidfw",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 21ec64bba931..f4296becf484 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -123,7 +123,6 @@ extern int register_android_view_InputApplicationHandle(JNIEnv* env);
extern int register_android_view_InputWindowHandle(JNIEnv* env);
extern int register_android_view_Surface(JNIEnv* env);
extern int register_android_view_SurfaceControl(JNIEnv* env);
-extern int register_android_view_SurfaceControlFpsListener(JNIEnv* env);
extern int register_android_view_SurfaceControlHdrLayerInfoListener(JNIEnv* env);
extern int register_android_view_SurfaceSession(JNIEnv* env);
extern int register_android_view_CompositionSamplingListener(JNIEnv* env);
@@ -1546,7 +1545,6 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_view_InputWindowHandle),
REG_JNI(register_android_view_Surface),
REG_JNI(register_android_view_SurfaceControl),
- REG_JNI(register_android_view_SurfaceControlFpsListener),
REG_JNI(register_android_view_SurfaceControlHdrLayerInfoListener),
REG_JNI(register_android_view_SurfaceSession),
REG_JNI(register_android_view_CompositionSamplingListener),
diff --git a/core/jni/OWNERS b/core/jni/OWNERS
index 4357729095af..9a460f58effe 100644
--- a/core/jni/OWNERS
+++ b/core/jni/OWNERS
@@ -76,6 +76,9 @@ per-file AndroidRuntime.cpp = file:/graphics/java/android/graphics/OWNERS
per-file AndroidRuntime.cpp = calin@google.com, ngeoffray@google.com, oth@google.com
# Although marked "view" this is mostly graphics stuff
per-file android_view_* = file:/graphics/java/android/graphics/OWNERS
+# File used for Android Studio layoutlib
+per-file LayoutlibLoader.cpp = file:/graphics/java/android/graphics/OWNERS
+per-file LayoutlibLoader.cpp = diegoperez@google.com, jgaillard@google.com
# Verity
per-file com_android_internal_security_Verity* = ebiggers@google.com, victorhsieh@google.com
diff --git a/core/jni/android_media_AudioAttributes.cpp b/core/jni/android_media_AudioAttributes.cpp
index 423ef7cd980b..d7fc1cc007a6 100644
--- a/core/jni/android_media_AudioAttributes.cpp
+++ b/core/jni/android_media_AudioAttributes.cpp
@@ -57,7 +57,7 @@ static struct {
jmethodID setUsage;
jmethodID setSystemUsage;
jmethodID setInternalCapturePreset;
- jmethodID setContentType;
+ jmethodID setInternalContentType;
jmethodID replaceFlags;
jmethodID addTag;
} gAudioAttributesBuilderMethods;
@@ -127,7 +127,7 @@ static jint nativeAudioAttributesToJavaAudioAttributes(
gAudioAttributesBuilderMethods.setInternalCapturePreset,
attributes.source);
env->CallObjectMethod(jAttributeBuilder.get(),
- gAudioAttributesBuilderMethods.setContentType,
+ gAudioAttributesBuilderMethods.setInternalContentType,
attributes.content_type);
env->CallObjectMethod(jAttributeBuilder.get(),
gAudioAttributesBuilderMethods.replaceFlags,
@@ -202,9 +202,9 @@ int register_android_media_AudioAttributes(JNIEnv *env)
gAudioAttributesBuilderMethods.setInternalCapturePreset = GetMethodIDOrDie(
env, audioAttributesBuilderClass, "setInternalCapturePreset",
"(I)Landroid/media/AudioAttributes$Builder;");
- gAudioAttributesBuilderMethods.setContentType = GetMethodIDOrDie(
- env, audioAttributesBuilderClass, "setContentType",
- "(I)Landroid/media/AudioAttributes$Builder;");
+ gAudioAttributesBuilderMethods.setInternalContentType =
+ GetMethodIDOrDie(env, audioAttributesBuilderClass, "setInternalContentType",
+ "(I)Landroid/media/AudioAttributes$Builder;");
gAudioAttributesBuilderMethods.replaceFlags = GetMethodIDOrDie(
env, audioAttributesBuilderClass, "replaceFlags",
"(I)Landroid/media/AudioAttributes$Builder;");
diff --git a/core/jni/android_text_Hyphenator.cpp b/core/jni/android_text_Hyphenator.cpp
index 3651dbdb3fa3..571a8e2e64be 100644
--- a/core/jni/android_text_Hyphenator.cpp
+++ b/core/jni/android_text_Hyphenator.cpp
@@ -127,6 +127,7 @@ static void init() {
addHyphenator("or", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX); // Oriya
addHyphenator("pa", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX); // Punjabi
addHyphenator("pt", 2, 3); // Portuguese
+ addHyphenator("ru", 2, 2); // Russian
addHyphenator("sk", 2, 2); // Slovak
addHyphenator("sl", 2, 2); // Slovenian
addHyphenator("sq", 2, 2); // Albanian
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index dd5af0435acc..d5470cc5a888 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -91,6 +91,7 @@ static struct {
jfieldID density;
jfieldID secure;
jfieldID deviceProductInfo;
+ jfieldID installOrientation;
} gStaticDisplayInfoClassInfo;
static struct {
@@ -1210,6 +1211,8 @@ static jobject nativeGetStaticDisplayInfo(JNIEnv* env, jclass clazz, jobject tok
env->SetBooleanField(object, gStaticDisplayInfoClassInfo.secure, info.secure);
env->SetObjectField(object, gStaticDisplayInfoClassInfo.deviceProductInfo,
convertDeviceProductInfoToJavaObject(env, info.deviceProductInfo));
+ env->SetIntField(object, gStaticDisplayInfoClassInfo.installOrientation,
+ static_cast<uint32_t>(info.installOrientation));
return object;
}
@@ -2152,6 +2155,8 @@ int register_android_view_SurfaceControl(JNIEnv* env)
gStaticDisplayInfoClassInfo.deviceProductInfo =
GetFieldIDOrDie(env, infoClazz, "deviceProductInfo",
"Landroid/hardware/display/DeviceProductInfo;");
+ gStaticDisplayInfoClassInfo.installOrientation =
+ GetFieldIDOrDie(env, infoClazz, "installOrientation", "I");
jclass dynamicInfoClazz = FindClassOrDie(env, "android/view/SurfaceControl$DynamicDisplayInfo");
gDynamicDisplayInfoClassInfo.clazz = MakeGlobalRefOrDie(env, dynamicInfoClazz);
diff --git a/core/jni/android_window_WindowInfosListener.cpp b/core/jni/android_window_WindowInfosListener.cpp
index 1381de567b55..08c9f20b0815 100644
--- a/core/jni/android_window_WindowInfosListener.cpp
+++ b/core/jni/android_window_WindowInfosListener.cpp
@@ -72,25 +72,29 @@ struct WindowInfosListener : public gui::WindowInfosListener {
return;
}
- jobjectArray jWindowHandlesArray =
- env->NewObjectArray(windowInfos.size(), gInputWindowHandleClass, nullptr);
+ ScopedLocalRef<jobjectArray>
+ jWindowHandlesArray(env,
+ env->NewObjectArray(windowInfos.size(), gInputWindowHandleClass,
+ nullptr));
for (int i = 0; i < windowInfos.size(); i++) {
ScopedLocalRef<jobject>
jWindowHandle(env,
android_view_InputWindowHandle_fromWindowInfo(env,
windowInfos[i]));
- env->SetObjectArrayElement(jWindowHandlesArray, i, jWindowHandle.get());
+ env->SetObjectArrayElement(jWindowHandlesArray.get(), i, jWindowHandle.get());
}
- jobjectArray jDisplayInfoArray =
- env->NewObjectArray(displayInfos.size(), gDisplayInfoClassInfo.clazz, nullptr);
+ ScopedLocalRef<jobjectArray>
+ jDisplayInfoArray(env,
+ env->NewObjectArray(displayInfos.size(),
+ gDisplayInfoClassInfo.clazz, nullptr));
for (int i = 0; i < displayInfos.size(); i++) {
ScopedLocalRef<jobject> jDisplayInfo(env, fromDisplayInfo(env, displayInfos[i]));
- env->SetObjectArrayElement(jDisplayInfoArray, i, jDisplayInfo.get());
+ env->SetObjectArrayElement(jDisplayInfoArray.get(), i, jDisplayInfo.get());
}
- env->CallVoidMethod(listener, gListenerClassInfo.onWindowInfosChanged, jWindowHandlesArray,
- jDisplayInfoArray);
+ env->CallVoidMethod(listener, gListenerClassInfo.onWindowInfosChanged,
+ jWindowHandlesArray.get(), jDisplayInfoArray.get());
env->DeleteGlobalRef(listener);
if (env->ExceptionCheck()) {
diff --git a/core/proto/OWNERS b/core/proto/OWNERS
index 78650ed34813..a4463e4e96c5 100644
--- a/core/proto/OWNERS
+++ b/core/proto/OWNERS
@@ -18,7 +18,8 @@ per-file usagestatsservice.proto, usagestatsservice_v2.proto = file:/core/java/a
per-file apphibernationservice.proto = file:/core/java/android/apphibernation/OWNERS
# Biometrics
-kchyn@google.com
+jaggies@google.com
+jbolinger@google.com
# Launcher
hyunyoungs@google.com
diff --git a/core/proto/android/server/vibrator/vibratormanagerservice.proto b/core/proto/android/server/vibrator/vibratormanagerservice.proto
index fbe2170ea51c..2f2158d4d5a0 100644
--- a/core/proto/android/server/vibrator/vibratormanagerservice.proto
+++ b/core/proto/android/server/vibrator/vibratormanagerservice.proto
@@ -97,7 +97,7 @@ message VibrationProto {
optional int32 status = 6;
}
-// Next id: 24
+// Next id: 25
message VibratorManagerServiceDumpProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
repeated int32 vibrator_ids = 1;
@@ -106,6 +106,7 @@ message VibratorManagerServiceDumpProto {
optional VibrationProto current_external_vibration = 4;
optional bool vibrator_under_external_control = 5;
optional bool low_power_mode = 6;
+ optional bool vibrate_on = 24;
optional int32 alarm_intensity = 18;
optional int32 alarm_default_intensity = 19;
optional int32 haptic_feedback_intensity = 7;
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 23453876c2d5..11560a5ccd1b 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -225,6 +225,7 @@ message DisplayContentProto {
repeated InsetsSourceProviderProto insets_source_providers = 35;
optional bool is_sleeping = 36;
repeated string sleep_tokens = 37;
+ repeated .android.graphics.RectProto keep_clear_areas = 38;
}
@@ -443,6 +444,7 @@ message WindowStateProto {
optional bool force_seamless_rotation = 42;
optional bool has_compat_scale = 43;
optional float global_scale = 44;
+ repeated .android.graphics.RectProto keep_clear_areas = 45;
}
message IdentifierProto {
diff --git a/core/proto/android/service/netstats.proto b/core/proto/android/service/netstats.proto
index c8cdfddc3985..ba2b6d6bd7e0 100644
--- a/core/proto/android/service/netstats.proto
+++ b/core/proto/android/service/netstats.proto
@@ -17,15 +17,11 @@
syntax = "proto2";
package android.service;
-import "frameworks/base/core/proto/android/privacy.proto";
-
option java_multiple_files = true;
option java_outer_classname = "NetworkStatsServiceProto";
// Represents dumpsys from NetworkStatsService (netstats).
message NetworkStatsServiceDumpProto {
- option (android.msg_privacy).dest = DEST_AUTOMATIC;
-
repeated NetworkInterfaceProto active_interfaces = 1;
repeated NetworkInterfaceProto active_uid_interfaces = 2;
@@ -45,8 +41,6 @@ message NetworkStatsServiceDumpProto {
// Corresponds to NetworkStatsService.mActiveIfaces/mActiveUidIfaces.
message NetworkInterfaceProto {
- option (android.msg_privacy).dest = DEST_AUTOMATIC;
-
// Name of the network interface (eg: wlan).
optional string interface = 1;
@@ -55,26 +49,14 @@ message NetworkInterfaceProto {
// Corresponds to NetworkIdentitySet.
message NetworkIdentitySetProto {
- option (android.msg_privacy).dest = DEST_AUTOMATIC;
-
repeated NetworkIdentityProto identities = 1;
}
// Corresponds to NetworkIdentity.
message NetworkIdentityProto {
- option (android.msg_privacy).dest = DEST_AUTOMATIC;
-
// Constants from ConnectivityManager.TYPE_*.
optional int32 type = 1;
- // Full subscriber ID on eng builds. The IMSI is scrubbed on user & userdebug
- // builds to only include the info about the GSM network operator (the info
- // that uniquely identifies the subscriber is removed).
- optional string subscriber_id = 2 [ (android.privacy).dest = DEST_EXPLICIT ];
-
- // Name of the network (eg: MyWifi).
- optional string network_id = 3 [ (android.privacy).dest = DEST_EXPLICIT ];
-
optional bool roaming = 4;
optional bool metered = 5;
@@ -86,8 +68,6 @@ message NetworkIdentityProto {
// Corresponds to NetworkStatsRecorder.
message NetworkStatsRecorderProto {
- option (android.msg_privacy).dest = DEST_AUTOMATIC;
-
optional int64 pending_total_bytes = 1;
optional NetworkStatsCollectionProto complete_history = 2;
@@ -95,15 +75,11 @@ message NetworkStatsRecorderProto {
// Corresponds to NetworkStatsCollection.
message NetworkStatsCollectionProto {
- option (android.msg_privacy).dest = DEST_AUTOMATIC;
-
repeated NetworkStatsCollectionStatsProto stats = 1;
}
// Corresponds to NetworkStatsCollection.mStats.
message NetworkStatsCollectionStatsProto {
- option (android.msg_privacy).dest = DEST_AUTOMATIC;
-
optional NetworkStatsCollectionKeyProto key = 1;
optional NetworkStatsHistoryProto history = 2;
@@ -111,8 +87,6 @@ message NetworkStatsCollectionStatsProto {
// Corresponds to NetworkStatsCollection.Key.
message NetworkStatsCollectionKeyProto {
- option (android.msg_privacy).dest = DEST_AUTOMATIC;
-
optional NetworkIdentitySetProto identity = 1;
optional int32 uid = 2;
@@ -124,8 +98,6 @@ message NetworkStatsCollectionKeyProto {
// Corresponds to NetworkStatsHistory.
message NetworkStatsHistoryProto {
- option (android.msg_privacy).dest = DEST_AUTOMATIC;
-
// Duration for this bucket in milliseconds.
optional int64 bucket_duration_ms = 1;
@@ -134,8 +106,6 @@ message NetworkStatsHistoryProto {
// Corresponds to each bucket in NetworkStatsHistory.
message NetworkStatsHistoryBucketProto {
- option (android.msg_privacy).dest = DEST_AUTOMATIC;
-
// Bucket start time in milliseconds since epoch.
optional int64 bucket_start_ms = 1;
diff --git a/core/proto/android/service/usb.proto b/core/proto/android/service/usb.proto
index 97097ff91219..c5eaf42784ab 100644
--- a/core/proto/android/service/usb.proto
+++ b/core/proto/android/service/usb.proto
@@ -196,9 +196,19 @@ message UsbIsHeadsetProto {
message UsbPortManagerProto {
option (android.msg_privacy).dest = DEST_AUTOMATIC;
+ enum HalVersion {
+ V_UNKNOWN = 0;
+ V1_0 = 10;
+ V1_1 = 11;
+ V1_2 = 12;
+ V1_3 = 13;
+ V2 = 20;
+ }
+
optional bool is_simulation_active = 1;
repeated UsbPortInfoProto usb_ports = 2;
optional bool enable_usb_data_signaling = 3;
+ optional HalVersion hal_version = 4;
}
message UsbPortInfoProto {
@@ -254,6 +264,9 @@ message UsbPortStatusProto {
optional DataRole data_role = 4;
repeated UsbPortStatusRoleCombinationProto role_combinations = 5;
optional android.service.ContaminantPresenceStatus contaminant_presence_status = 6;
+ optional string usb_data_status = 7;
+ optional bool is_power_transfer_limited = 8;
+ optional string usb_power_brick_status = 9;
}
message UsbPortStatusRoleCombinationProto {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index ae2052b66f9c..3a842ee6e7f3 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4251,7 +4251,8 @@
<!-- Allows low-level access to setting the keyboard layout.
<p>Not for use by third-party applications.
- @hide -->
+ @hide
+ @TestApi -->
<permission android:name="android.permission.SET_KEYBOARD_LAYOUT"
android:protectionLevel="signature" />
@@ -4745,6 +4746,12 @@
<permission android:name="android.permission.CAPTURE_AUDIO_HOTWORD"
android:protectionLevel="signature|privileged|role" />
+ <!-- @SystemApi Allows an application to access the ultrasound content.
+ <p>Not for use by third-party applications.</p>
+ @hide -->
+ <permission android:name="android.permission.ACCESS_ULTRASOUND"
+ android:protectionLevel="signature|privileged" />
+
<!-- Puts an application in the chain of trust for sound trigger
operations. Being in the chain of trust allows an application to
delegate an identity of a separate entity to the sound trigger system
@@ -5122,6 +5129,11 @@
<permission android:name="android.permission.SET_WALLPAPER_COMPONENT"
android:protectionLevel="signature|privileged" />
+ <!-- @SystemApi Allows applications to set the wallpaper dim amount.
+ @hide. -->
+ <permission android:name="android.permission.SET_WALLPAPER_DIM_AMOUNT"
+ android:protectionLevel="signature|privileged" />
+
<!-- @SystemApi Allows applications to read dream settings and dream state.
@hide -->
<permission android:name="android.permission.READ_DREAM_STATE"
@@ -6032,10 +6044,15 @@
<permission android:name="android.permission.MANAGE_TOAST_RATE_LIMITING"
android:protectionLevel="signature" />
- <!-- Allows managing the Game Mode
- @hide Used internally. -->
+ <!-- @SystemApi Allows managing the Game Mode
+ @hide -->
<permission android:name="android.permission.MANAGE_GAME_MODE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Allows accessing the frame rate per second of a given application
+ @hide -->
+ <permission android:name="android.permission.ACCESS_FPS_COUNTER"
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows the holder to register callbacks to inform the RebootReadinessManager
when they are performing reboot-blocking work.
@@ -6094,11 +6111,21 @@
<permission android:name="android.permission.CAPTURE_BLACKOUT_CONTENT"
android:protectionLevel="signature" />
- <!-- @SystemApi Allows an application to query over global data in AppSearch.
+ <!-- @SystemApi Allows an application to query over global data in AppSearch.
@hide -->
<permission android:name="android.permission.READ_GLOBAL_APP_SEARCH_DATA"
android:protectionLevel="internal|role" />
+ <!-- Allows an application to query over global data in AppSearch that's visible to the
+ ASSISTANT role. -->
+ <permission android:name="android.permission.READ_ASSISTANT_APP_SEARCH_DATA"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to query over global data in AppSearch that's visible to the
+ HOME role. -->
+ <permission android:name="android.permission.READ_HOME_APP_SEARCH_DATA"
+ android:protectionLevel="internal|role" />
+
<!-- @SystemApi Allows an application to create virtual devices in VirtualDeviceManager.
@hide -->
<permission android:name="android.permission.CREATE_VIRTUAL_DEVICE"
@@ -6112,7 +6139,7 @@
<permission android:name="android.permission.SEND_SAFETY_CENTER_UPDATE"
android:protectionLevel="signature|privileged" />
- <!-- Allows an application to launch device manager setup screens.
+ <!-- @SystemApi Allows an application to launch device manager setup screens.
<p>Not for use by third-party applications.
@hide
-->
@@ -6140,6 +6167,19 @@
<permission android:name="android.permission.MANAGE_SAFETY_CENTER"
android:protectionLevel="internal|installer|role" />
+ <!-- @SystemApi Allows an application to access the AmbientContextEvent service.
+ @hide
+ -->
+ <permission android:name="android.permission.ACCESS_AMBIENT_CONTEXT_EVENT"
+ android:protectionLevel="internal|role"/>
+
+ <!-- @SystemApi Required by a AmbientContextEventDetectionService
+ to ensure that only the service with this permission can bind to it.
+ @hide
+ -->
+ <permission android:name="android.permission.BIND_AMBIENT_CONTEXT_DETECTION_SERVICE"
+ android:protectionLevel="signature" />
+
<!-- Attribution for Geofencing service. -->
<attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/>
<!-- Attribution for Country Detector. -->
diff --git a/core/res/OWNERS b/core/res/OWNERS
index b18a9896cb2a..4bea4d55e5ef 100644
--- a/core/res/OWNERS
+++ b/core/res/OWNERS
@@ -34,3 +34,7 @@ per-file res/values/dimens_car.xml = file:/platform/packages/services/Car:/OWNER
# Wear
per-file res/*-watch/* = file:/platform/frameworks/opt/wear:/OWNERS
+
+# PowerProfile
+per-file res/xml/power_profile.xml = file:/BATTERY_STATS_OWNERS
+per-file res/xml/power_profile_test.xml = file:/BATTERY_STATS_OWNERS
diff --git a/core/res/res/drawable/ic_ime_nav_back.xml b/core/res/res/drawable/ic_ime_nav_back.xml
new file mode 100644
index 000000000000..ca329aa64bd4
--- /dev/null
+++ b/core/res/res/drawable/ic_ime_nav_back.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="28dp"
+ android:height="28dp"
+ android:autoMirrored="true"
+ android:viewportWidth="28"
+ android:viewportHeight="28">
+ <path
+ android:pathData="M16.78,10.03l-3.97,3.97l3.97,3.97l-1.06,1.06l-5.03,-5.03l5.03,-5.03z"
+ android:fillColor="#FFFFFFFF" />
+</vector> \ No newline at end of file
diff --git a/core/res/res/drawable/ic_ime_switcher.xml b/core/res/res/drawable/ic_ime_switcher.xml
new file mode 100644
index 000000000000..6c3b76627323
--- /dev/null
+++ b/core/res/res/drawable/ic_ime_switcher.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="20dp"
+ android:height="20dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M19,7h2v2h-2V7zM15,7h2v2h-2V7zM3,7h2v2H3V7zM7,7h2v2H7V7zM11,7h2v2h-2V7zM19,11h2v2h-2V11zM15,11h2v2h-2V11zM3,11h2v2H3V11zM7,11h2v2H7V11zM11,11h2v2h-2V11zM7,15h10v2H7V15z"
+ android:fillColor="#FFFFFFFF"/>
+</vector>
diff --git a/core/res/res/layout/input_method_nav_back.xml b/core/res/res/layout/input_method_nav_back.xml
new file mode 100644
index 000000000000..671766aec281
--- /dev/null
+++ b/core/res/res/layout/input_method_nav_back.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<android.inputmethodservice.navigationbar.KeyButtonView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/input_method_nav_back"
+ android:layout_width="@dimen/input_method_navigation_key_width"
+ android:layout_height="match_parent"
+ android:layout_weight="0"
+ android:scaleType="center"
+ android:contentDescription="@string/input_method_nav_back_button_desc"
+ android:paddingStart="@dimen/input_method_navigation_key_padding"
+ android:paddingEnd="@dimen/input_method_navigation_key_padding"
+ />
diff --git a/core/res/res/layout/input_method_nav_home_handle.xml b/core/res/res/layout/input_method_nav_home_handle.xml
new file mode 100644
index 000000000000..501f5126aff8
--- /dev/null
+++ b/core/res/res/layout/input_method_nav_home_handle.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<android.inputmethodservice.navigationbar.NavigationHandle
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/input_method_nav_home_handle"
+ android:layout_width="72dp"
+ android:layout_height="match_parent"
+ android:layout_weight="0"
+ android:paddingStart="@dimen/input_method_navigation_key_padding"
+ android:paddingEnd="@dimen/input_method_navigation_key_padding"
+ />
diff --git a/core/res/res/layout/input_method_nav_ime_switcher.xml b/core/res/res/layout/input_method_nav_ime_switcher.xml
new file mode 100644
index 000000000000..b571ba927837
--- /dev/null
+++ b/core/res/res/layout/input_method_nav_ime_switcher.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<android.inputmethodservice.navigationbar.KeyButtonView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/input_method_nav_ime_switcher"
+ android:layout_width="@dimen/input_method_navigation_key_width"
+ android:layout_height="match_parent"
+ android:layout_weight="0"
+ android:contentDescription="@string/input_method_ime_switch_button_desc"
+ android:scaleType="center"
+ android:paddingStart="@dimen/input_method_navigation_key_padding"
+ android:paddingEnd="@dimen/input_method_navigation_key_padding"
+ />
diff --git a/core/res/res/layout/input_method_navigation_bar.xml b/core/res/res/layout/input_method_navigation_bar.xml
new file mode 100644
index 000000000000..ce402fbce891
--- /dev/null
+++ b/core/res/res/layout/input_method_navigation_bar.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<android.inputmethodservice.navigationbar.NavigationBarView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/input_method_navigation_bar_view"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:clipChildren="false"
+ android:clipToPadding="false">
+
+ <android.inputmethodservice.navigationbar.NavigationBarInflaterView
+ android:id="@+id/input_method_nav_inflater"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:clipChildren="false"
+ android:clipToPadding="false" />
+
+</android.inputmethodservice.navigationbar.NavigationBarView>
diff --git a/core/res/res/layout/input_method_navigation_layout.xml b/core/res/res/layout/input_method_navigation_layout.xml
new file mode 100644
index 000000000000..05e750ad34ff
--- /dev/null
+++ b/core/res/res/layout/input_method_navigation_layout.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_marginStart="@dimen/input_method_rounded_corner_content_padding"
+ android:layout_marginEnd="@dimen/input_method_rounded_corner_content_padding"
+ android:paddingStart="@dimen/input_method_nav_content_padding"
+ android:paddingEnd="@dimen/input_method_nav_content_padding"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:id="@+id/input_method_nav_horizontal">
+
+ <FrameLayout
+ android:id="@+id/input_method_nav_buttons"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:clipChildren="false"
+ android:clipToPadding="false">
+
+ <LinearLayout
+ android:id="@+id/input_method_nav_ends_group"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="horizontal"
+ android:clipToPadding="false"
+ android:clipChildren="false" />
+
+ <LinearLayout
+ android:id="@+id/input_method_nav_center_group"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_gravity="center"
+ android:gravity="center"
+ android:orientation="horizontal"
+ android:clipToPadding="false"
+ android:clipChildren="false" />
+
+ </FrameLayout>
+
+</FrameLayout>
diff --git a/core/res/res/values-sw360dp/dimens.xml b/core/res/res/values-sw360dp/dimens.xml
index 4c74264c676a..00de60efbeb2 100644
--- a/core/res/res/values-sw360dp/dimens.xml
+++ b/core/res/res/values-sw360dp/dimens.xml
@@ -18,4 +18,8 @@
-->
<resources>
<dimen name="chooser_grid_padding">16dp</dimen>
+
+ <!-- Copied from SysUI's @dimen/navigation_key_width for the embedded nav bar in the IME -->
+ <dimen name="input_method_navigation_key_width">80dip</dimen>
+
</resources>
diff --git a/core/res/res/values-sw372dp/dimens.xml b/core/res/res/values-sw372dp/dimens.xml
new file mode 100644
index 000000000000..cb29a1944a22
--- /dev/null
+++ b/core/res/res/values-sw372dp/dimens.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+ <!-- Copied from SysUI's @dimen/nav_content_padding for the embedded nav bar in the IME -->
+ <dimen name="input_method_nav_content_padding">8dp</dimen>
+</resources>
diff --git a/core/res/res/values-sw600dp/dimens.xml b/core/res/res/values-sw600dp/dimens.xml
index e8f15fd022d7..4c70ea32bb5b 100644
--- a/core/res/res/values-sw600dp/dimens.xml
+++ b/core/res/res/values-sw600dp/dimens.xml
@@ -41,6 +41,11 @@
<!-- Size of lockscreen outerring on unsecure unlock LockScreen -->
<dimen name="keyguard_lockscreen_outerring_diameter">364dp</dimen>
+ <!-- Copied from SysUI's @dimen/navigation_key_width for the embedded nav bar in the IME -->
+ <dimen name="input_method_navigation_key_width">128dp</dimen>
+ <!-- Copied from SysUI's @dimen/navigation_key_padding for the embedded nav bar in the IME -->
+ <dimen name="input_method_navigation_key_padding">25dp</dimen>
+
<!-- Height of FaceUnlock view in keyguard -->
<dimen name="face_unlock_height">430dip</dimen>
diff --git a/core/res/res/values-sw900dp/dimens.xml b/core/res/res/values-sw900dp/dimens.xml
index 11092b2cb9e9..9ec420453f6b 100644
--- a/core/res/res/values-sw900dp/dimens.xml
+++ b/core/res/res/values-sw900dp/dimens.xml
@@ -24,4 +24,12 @@
the same as @dimen/navigation_bar_height -->
<dimen name="navigation_bar_height_landscape">56dp</dimen>
+ <!-- Copied from SysUI's @dimen/navigation_key_width for the embedded nav bar in the IME. -->
+ <dimen name="input_method_navigation_key_width">80dp</dimen>
+ <!-- Copied from SysUI's @dimen/navigation_key_padding for the embedded nav bar in the IME. -->
+ <dimen name="input_method_navigation_key_padding">0dp</dimen>
+ <!-- Copied from SysUI's @dimen/key_button_ripple_max_width for the embedded nav bar in the
+ IME. -->
+ <dimen name="input_method_nav_key_button_ripple_max_width">76dp</dimen>
+
</resources>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index e232d858bb43..8696f5a94750 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3347,6 +3347,14 @@
<p>Note that this flag will only be respected if the View's Outline returns true from
{@link android.graphics.Outline#canClip()}. -->
<attr name="clipToOutline" format="boolean" />
+
+ <!-- <p> Sets a preference to keep the bounds of this view clear from floating windows
+ above this view's window. This informs the system that the view is considered a vital
+ area for the user and that ideally it should not be covered. Setting this is only
+ appropriate for UI where the user would likely take action to uncover it.
+ <p>The system will try to respect this, but when not possible will ignore it.
+ See {@link android.view.View#setPreferKeepClear}. -->
+ <attr name="preferKeepClear" format="boolean" />
</declare-styleable>
<!-- Attributes that can be assigned to a tag for a particular View. -->
@@ -5030,6 +5038,10 @@
<attr name="fontFeatureSettings" format="string" />
<!-- Font variation settings. -->
<attr name="fontVariationSettings" format="string"/>
+ <!-- Specifies the strictness of line-breaking rules applied within an element. -->
+ <attr name="lineBreakStyle" />
+ <!-- Specifies the phrase-based breaking opportunities. -->
+ <attr name="lineBreakWordStyle" />
</declare-styleable>
<declare-styleable name="TextClock">
<!-- Specifies the formatting pattern used to show the time and/or date
@@ -5428,6 +5440,13 @@
<!-- ndicates breaking text with the most strictest line-breaking rules. -->
<enum name="strict" value="3" />
</attr>
+ <!-- Specify the phrase-based line break can be used when calculating the text wrapping.-->
+ <attr name="lineBreakWordStyle">
+ <!-- No line break word style specific. -->
+ <enum name="none" value="0" />
+ <!-- Specify the phrase based breaking. -->
+ <enum name="phrase" value="1" />
+ </attr>
<!-- Specify the type of auto-size. Note that this feature is not supported by EditText,
works only for TextView. -->
<attr name="autoSizeTextType" format="enum">
@@ -9376,11 +9395,12 @@
<attr name="canPauseRecording" format="boolean" />
</declare-styleable>
- <!-- Use <code>tv-iapp</code> as the root tag of the XML resource that describes a
- {@link android.media.tv.interactive.TvIAppService}, which is referenced from its
- {@link android.media.tv.interactive.TvIAppService#SERVICE_META_DATA} meta-data entry.
- Described here are the attributes that can be included in that tag. -->
- <declare-styleable name="TvIAppService">
+ <!-- Use <code>tv-interactive-app</code> as the root tag of the XML resource that describes a
+ {@link android.media.tv.interactive.TvInteractiveAppService}, which is referenced
+ from its
+ {@link android.media.tv.interactive.TvInteractiveAppService#SERVICE_META_DATA}
+ meta-data entry. Described here are the attributes that can be included in that tag. -->
+ <declare-styleable name="TvInteractiveAppService">
<!-- The interactive app types that the TV interactive app service supports.
Reference to a string array resource that describes the supported types,
e.g. HbbTv, Ginga. -->
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index a5954338910d..3a2fb6e70ba8 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -401,6 +401,15 @@
and before. -->
<attr name="sharedUserMaxSdkVersion" format="integer" />
+ <!-- Whether the application should inherit all AndroidKeyStore keys of its shared user
+ group in the case of leaving its shared user ID in an upgrade. If set to false, all
+ AndroidKeyStore keys will remain in the shared user group, and the application will no
+ longer have access to those keys after the upgrade. If set to true, all AndroidKeyStore
+ keys owned by the shared user group will be transferred to the upgraded application;
+ other applications in the shared user group will no longer have access to those keys
+ after the migration. The default value is false if not explicitly set. -->
+ <attr name="inheritKeyStoreKeys" format="boolean" />
+
<!-- Internal version code. This is the number used to determine whether
one version is more recent than another: it has no other meaning than
that higher numbers are more recent. You could use this number to
@@ -1677,6 +1686,7 @@
<attr name="sharedUserId" />
<attr name="sharedUserLabel" />
<attr name="sharedUserMaxSdkVersion" />
+ <attr name="inheritKeyStoreKeys" />
<attr name="installLocation" />
<attr name="isolatedSplits" />
<attr name="isFeatureSplit" />
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index f4b7b73f4a6e..c0c8618aaf96 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2135,6 +2135,11 @@
<string name="config_systemAppProtectionService" translatable="false"></string>
<!-- The name of the package that will hold the system calendar sync manager role. -->
<string name="config_systemAutomotiveCalendarSyncManager" translatable="false"></string>
+ <!-- The name of the package that will hold the default automotive navigation role. -->
+ <string name="config_defaultAutomotiveNavigation" translatable="false"></string>
+
+ <!-- The name of the package that will handle updating the device management role. -->
+ <string name="config_deviceManagerUpdater" translatable="false"></string>
<!-- The name of the package that will be allowed to change its components' label/icon. -->
<string name="config_overrideComponentUiPackage" translatable="false">com.android.stk</string>
@@ -4072,6 +4077,12 @@
-->
<string name="config_defaultRotationResolverService" translatable="false"></string>
+ <!-- The component name for the default system AmbientContextEvent detection service.
+ This service must be trusted, as it can be activated without explicit consent of the user.
+ See android.service.ambientcontext.AmbientContextDetectionService.
+ -->
+ <string name="config_defaultAmbientContextDetectionService" translatable="false"></string>
+
<!-- The component name for the system-wide captions service.
This service must be trusted, as it controls part of the UI of the volume bar.
Example: "com.android.captions/.SystemCaptionsService"
@@ -4242,7 +4253,7 @@
<string translatable="false" name="config_defaultRingtoneVibrationSound"></string>
<!-- Default number of notifications from the same app before they are automatically grouped by the OS -->
- <integer translatable="false" name="config_autoGroupAtCount">4</integer>
+ <integer translatable="false" name="config_autoGroupAtCount">2</integer>
<!-- The OEM specified sensor type for the lift trigger to launch the camera app. -->
<integer name="config_cameraLiftTriggerSensorType">-1</integer>
@@ -5607,4 +5618,7 @@
<!-- Flag indicating if help links for Settings app should be enabled. -->
<bool name="config_settingsHelpLinksEnabled">false</bool>
+
+ <!-- Whether or not to enable the lock screen entry point for the QR code scanner. -->
+ <bool name="config_enableQrCodeScannerOnLockScreen">false</bool>
</resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index a877bd39bceb..3f08e4b9d9ad 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -131,6 +131,19 @@
corners. -->
<dimen name="rounded_corner_radius_bottom_adjustment">0px</dimen>
+ <!-- Copied from SysUI's @dimen/navigation_key_width for the embedded nav bar in the IME. -->
+ <dimen name="input_method_navigation_key_width">70dp</dimen>
+ <!-- Copied from SysUI's @dimen/navigation_key_padding for the embedded nav bar in the IME. -->
+ <dimen name="input_method_navigation_key_padding">0dp</dimen>
+ <!-- Copied from SysUI's @dimen/nav_content_padding for the embedded nav bar in the IME. -->
+ <dimen name="input_method_nav_content_padding">0px</dimen>
+ <!-- Copied from SysUI's @dimen/rounded_corner_content_padding for the embedded nav bar in the
+ IME. -->
+ <dimen name="input_method_rounded_corner_content_padding">0px</dimen>
+ <!-- Copied from SysUI's @dimen/key_button_ripple_max_width for the embedded nav bar in the
+ IME. -->
+ <dimen name="input_method_nav_key_button_ripple_max_width">95dp</dimen>
+
<!-- Width of the window of the divider bar used to resize docked stacks. -->
<dimen name="docked_stack_divider_thickness">48dp</dimen>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index e83f4a66883c..505fe59cfefd 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3212,6 +3212,7 @@
<public type="attr" name="shouldUseDefaultUnfoldTransition" id="0x0101064c" />
<public type="attr" name="lineBreakStyle" id="0x0101064d" />
+ <public type="attr" name="lineBreakWordStyle" id="0x0101064e" />
<staging-public-group-final type="id" first-id="0x01fe0000">
<public name="accessibilityActionDragStart" />
@@ -3256,6 +3257,8 @@
<public name="gameSessionService" />
<public name="localeConfig" />
<public name="showBackground" />
+ <public name="inheritKeyStoreKeys" />
+ <public name="preferKeepClear" />
</staging-public-group>
<staging-public-group type="id" first-id="0x01de0000">
@@ -3279,6 +3282,8 @@
<public name="config_systemAppProtectionService" />
<!-- @hide @SystemApi @TestApi -->
<public name="config_systemAutomotiveCalendarSyncManager" />
+ <!-- @hide @SystemApi -->
+ <public name="config_defaultAutomotiveNavigation" />
</staging-public-group>
<staging-public-group type="dimen" first-id="0x01db0000">
@@ -3322,6 +3327,8 @@
<staging-public-group type="bool" first-id="0x01cf0000">
<!-- @hide @TestApi -->
<public name="config_preventImeStartupUnlessTextEditor" />
+ <!-- @hide @SystemApi -->
+ <public name="config_enableQrCodeScannerOnLockScreen" />
</staging-public-group>
<staging-public-group type="fraction" first-id="0x01ce0000">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 6577ebc8cd3f..1a5d8b7ca8fd 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3277,6 +3277,11 @@
<!-- Title for EditText context menu [CHAR LIMIT=20] -->
<string name="editTextMenuTitle">Text actions</string>
+ <!-- Content description of the back button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
+ <string name="input_method_nav_back_button_desc">Back</string>
+ <!-- Content description of the switch input method button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
+ <string name="input_method_ime_switch_button_desc">Switch input method</string>
+
<!-- If the device is getting low on internal storage, a notification is shown to the user. This is the title of that notification. -->
<string name="low_internal_storage_view_title">Storage space running out</string>
<!-- If the device is getting low on internal storage, a notification is shown to the user. This is the message of that notification. -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 6bc35ec2fffe..8c8ef120f781 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2439,6 +2439,24 @@
<!-- From PinyinIME(!!!) -->
<java-symbol type="string" name="inputMethod" />
+ <!-- Gestural Nav buttons within InputMethodService -->
+ <java-symbol type="dimen" name="input_method_nav_key_button_ripple_max_width" />
+ <java-symbol type="drawable" name="ic_ime_nav_back" />
+ <java-symbol type="drawable" name="ic_ime_switcher" />
+ <java-symbol type="id" name="input_method_nav_back" />
+ <java-symbol type="id" name="input_method_nav_buttons" />
+ <java-symbol type="id" name="input_method_nav_center_group" />
+ <java-symbol type="id" name="input_method_nav_ends_group" />
+ <java-symbol type="id" name="input_method_nav_home_handle" />
+ <java-symbol type="id" name="input_method_nav_horizontal" />
+ <java-symbol type="id" name="input_method_nav_ime_switcher" />
+ <java-symbol type="id" name="input_method_nav_inflater" />
+ <java-symbol type="layout" name="input_method_navigation_bar" />
+ <java-symbol type="layout" name="input_method_navigation_layout" />
+ <java-symbol type="layout" name="input_method_nav_back" />
+ <java-symbol type="layout" name="input_method_nav_home_handle" />
+ <java-symbol type="layout" name="input_method_nav_ime_switcher" />
+
<!-- From Chromium-WebView -->
<java-symbol type="attr" name="actionModeWebSearchDrawable" />
<java-symbol type="string" name="websearch" />
@@ -3652,6 +3670,7 @@
<java-symbol type="string" name="config_defaultRotationResolverService" />
<java-symbol type="string" name="config_defaultSystemCaptionsService" />
<java-symbol type="string" name="config_defaultSystemCaptionsManagerService" />
+ <java-symbol type="string" name="config_defaultAmbientContextDetectionService" />
<java-symbol type="string" name="config_retailDemoPackage" />
<java-symbol type="string" name="config_retailDemoPackageSignature" />
@@ -4646,4 +4665,6 @@
<java-symbol type="string" name="config_supervisedUserCreationPackage"/>
<java-symbol type="bool" name="config_enableSafetyCenter" />
+
+ <java-symbol type="string" name="config_deviceManagerUpdater" />
</resources>
diff --git a/core/res/res/xml/power_profile.xml b/core/res/res/xml/power_profile.xml
index d310736ae121..fc63657f04d0 100644
--- a/core/res/res/xml/power_profile.xml
+++ b/core/res/res/xml/power_profile.xml
@@ -144,17 +144,49 @@
<value>2</value> <!-- 4097-/hr -->
</array>
- <!-- Cellular modem related values. Default is 0.-->
- <item name="modem.controller.sleep">0</item>
- <item name="modem.controller.idle">0</item>
- <item name="modem.controller.rx">0</item>
- <array name="modem.controller.tx"> <!-- Strength 0 to 4 -->
- <value>0</value>
- <value>0</value>
- <value>0</value>
- <value>0</value>
- <value>0</value>
- </array>
+ <!-- Cellular modem related values.-->
+ <modem>
+ <!-- Modem sleep drain current value in mA. -->
+ <sleep>0</sleep>
+ <!-- Modem idle drain current value in mA. -->
+ <idle>0</idle>
+ <!-- Modem active drain current values.
+ Multiple <active /> can be defined to specify current drain for different modes of
+ operation.
+ Available attributes:
+ rat - Specify the current drain for a Radio Access Technology.
+ Available options are "LTE", "NR" and "DEFAULT".
+ <active rat="default" /> will be used for any usage that does not match any other
+ defined <active /> rat.
+
+ nrFrequency - Specify the current drain for a frequency level while NR is active.
+ Available options are "LOW", "MID", "HIGH", "MMWAVE", and "DEFAULT",
+ where,
+ "LOW" indicated <1GHz frequencies,
+ "MID" indicates 1GHz to 3GHz frequencies,
+ "HIGH" indicates 3GHz to 6GHz frequencies,
+ "MMWAVE"indicates >6GHz frequencies.
+ <active rat="NR" nrFrequency="default"/> will be used for any usage that
+ does not match any other defined <active rat="NR" /> nrFrequency.
+ -->
+ <active rat="DEFAULT">
+ <!-- Transmit current drain in mA. -->
+ <receive>0</receive>
+
+ <!-- Transmit current drains in mA. Must be defined for all levels (0 to 4) -->
+ <transmit level="0">0</transmit>
+ <transmit level="1">0</transmit>
+ <transmit level="2">0</transmit>
+ <transmit level="3">0</transmit>
+ <transmit level="4">0</transmit>
+ </active>
+ <!-- Additional <active /> may be defined.
+ Example:
+ <active rat="LTE"> ... </active>
+ <active rat="NR" nrFrequency="MMWAVE"> ... </active>
+ <active rat="NR" nrFrequency="DEFAULT"> ... </active>
+ -->
+ </modem>
<item name="modem.controller.voltage">0</item>
<!-- GPS related values. Default is 0.-->
@@ -163,5 +195,4 @@
<value>0</value>
</array>
<item name="gps.voltage">0</item>
-
</device>
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index f2b35c72a567..a80424e500c4 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -38,6 +38,7 @@
<uses-permission android:name="android.permission.ACCESS_CACHE_FILESYSTEM" />
<uses-permission android:name="android.permission.ACCESS_DOWNLOAD_MANAGER" />
<uses-permission android:name="android.permission.ACCESS_DOWNLOAD_MANAGER_ADVANCED" />
+ <uses-permission android:name="android.permission.ACCESS_FPS_COUNTER" />
<uses-permission android:name="android.permission.DOWNLOAD_CACHE_NON_PURGEABLE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
@@ -64,6 +65,7 @@
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
<uses-permission android:name="android.permission.READ_DREAM_STATE" />
+ <uses-permission android:name="android.permission.REAL_GET_TASKS"/>
<uses-permission android:name="android.permission.WRITE_DREAM_STATE" />
<uses-permission android:name="android.permission.READ_CLIPBOARD_IN_BACKGROUND" />
<uses-permission android:name="android.permission.READ_LOGS"/>
diff --git a/core/tests/coretests/res/xml/power_profile_test.xml b/core/tests/coretests/res/xml/power_profile_test.xml
new file mode 100644
index 000000000000..22571142a350
--- /dev/null
+++ b/core/tests/coretests/res/xml/power_profile_test.xml
@@ -0,0 +1,123 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<device name="Android">
+ <!-- This is the battery capacity in mAh -->
+ <item name="battery.capacity">3000</item>
+
+ <!-- Number of cores each CPU cluster contains -->
+ <array name="cpu.clusters.cores">
+ <value>4</value> <!-- Cluster 0 has 4 cores (cpu0, cpu1, cpu2, cpu3) -->
+ <value>4</value> <!-- Cluster 1 has 4 cores (cpu4, cpu5, cpu5, cpu7) -->
+ </array>
+
+ <!-- Power consumption when CPU is suspended -->
+ <item name="cpu.suspend">5</item>
+ <!-- Additional power consumption when CPU is in a kernel idle loop -->
+ <item name="cpu.idle">1.11</item>
+ <!-- Additional power consumption by CPU excluding cluster and core when running -->
+ <item name="cpu.active">2.55</item>
+
+ <!-- Additional power consumption by CPU cluster0 itself when running excluding cores in it -->
+ <item name="cpu.cluster_power.cluster0">2.11</item>
+ <!-- Additional power consumption by CPU cluster1 itself when running excluding cores in it -->
+ <item name="cpu.cluster_power.cluster1">2.22</item>
+
+ <!-- Different CPU speeds as reported in
+ /sys/devices/system/cpu/cpu0/cpufreq/stats/scaling_available_frequencies -->
+ <array name="cpu.core_speeds.cluster0">
+ <value>300000</value> <!-- 300 MHz CPU speed -->
+ <value>1000000</value> <!-- 1000 MHz CPU speed -->
+ <value>2000000</value> <!-- 2000 MHz CPU speed -->
+ </array>
+ <!-- Different CPU speeds as reported in
+ /sys/devices/system/cpu/cpu4/cpufreq/stats/scaling_available_frequencies -->
+ <array name="cpu.core_speeds.cluster1">
+ <value>300000</value> <!-- 300 MHz CPU speed -->
+ <value>1000000</value> <!-- 1000 MHz CPU speed -->
+ <value>2500000</value> <!-- 2500 MHz CPU speed -->
+ <value>3000000</value> <!-- 3000 MHz CPU speed -->
+ </array>
+
+ <!-- Additional power used by a CPU from cluster 0 when running at different
+ speeds. Currently this measurement also includes cluster cost. -->
+ <array name="cpu.core_power.cluster0">
+ <value>10</value> <!-- 300 MHz CPU speed -->
+ <value>20</value> <!-- 1000 MHz CPU speed -->
+ <value>30</value> <!-- 1900 MHz CPU speed -->
+ </array>
+ <!-- Additional power used by a CPU from cluster 1 when running at different
+ speeds. Currently this measurement also includes cluster cost. -->
+ <array name="cpu.core_power.cluster1">
+ <value>25</value> <!-- 300 MHz CPU speed -->
+ <value>35</value> <!-- 1000 MHz CPU speed -->
+ <value>50</value> <!-- 2500 MHz CPU speed -->
+ <value>60</value> <!-- 3000 MHz CPU speed -->
+ </array>
+
+ <!-- Power used by display unit in ambient display mode, including back lighting-->
+ <item name="ambient.on">0.5</item>
+ <!-- Additional power used when screen is turned on at minimum brightness -->
+ <item name="screen.on">100</item>
+ <!-- Additional power used when screen is at maximum brightness, compared to
+ screen at minimum brightness -->
+ <item name="screen.full">800</item>
+
+ <!-- Average power used by the camera flash module when on -->
+ <item name="camera.flashlight">500</item>
+ <!-- Average power use by the camera subsystem for a typical camera
+ application. Intended as a rough estimate for an application running a
+ preview and capturing approximately 10 full-resolution pictures per
+ minute. -->
+ <item name="camera.avg">600</item>
+
+ <!-- Additional power used by the audio hardware, probably due to DSP -->
+ <item name="audio">100.0</item>
+
+ <!-- Additional power used by the video hardware, probably due to DSP -->
+ <item name="video">150.0</item> <!-- ~50mA -->
+
+ <!-- Additional power used when GPS is acquiring a signal -->
+ <item name="gps.on">10</item>
+
+ <!-- Additional power used when cellular radio is transmitting/receiving -->
+ <item name="radio.active">60</item>
+ <!-- Additional power used when cellular radio is paging the tower -->
+ <item name="radio.scanning">3</item>
+ <!-- Additional power used when the cellular radio is on. Multi-value entry,
+ one per signal strength (no signal, weak, moderate, strong) -->
+ <array name="radio.on"> <!-- Strength 0 to BINS-1 -->
+ <value>6</value> <!-- none -->
+ <value>5</value> <!-- poor -->
+ <value>4</value> <!-- moderate -->
+ <value>3</value> <!-- good -->
+ <value>3</value> <!-- great -->
+ </array>
+
+ <!-- Cellular modem related values. These constants are deprecated, but still supported and
+ need to be tested -->
+ <item name="modem.controller.sleep">123</item>
+ <item name="modem.controller.idle">456</item>
+ <item name="modem.controller.rx">789</item>
+ <array name="modem.controller.tx"> <!-- Strength 0 to 4 -->
+ <value>10</value>
+ <value>20</value>
+ <value>30</value>
+ <value>40</value>
+ <value>50</value>
+ </array>
+</device> \ No newline at end of file
diff --git a/core/tests/coretests/res/xml/power_profile_test_modem.xml b/core/tests/coretests/res/xml/power_profile_test_modem.xml
new file mode 100644
index 000000000000..ff36a9c94e0b
--- /dev/null
+++ b/core/tests/coretests/res/xml/power_profile_test_modem.xml
@@ -0,0 +1,151 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** 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.
+*/
+-->
+
+<device name="test">
+ <test-modem name="testModemPowerProfile_defaultRat">
+ <!-- Modem sleep drain current value in mA. -->
+ <sleep>10</sleep>
+ <!-- Modem idle drain current value in mA. -->
+ <idle>20</idle>
+ <active rat="DEFAULT">
+ <!-- Transmit current drain in mA. -->
+ <receive>30</receive>
+ <!-- Transmit current drains in mA. Must be defined for all levels (0 to 4) -->
+ <transmit level="0">40</transmit>
+ <transmit level="1">50</transmit>
+ <transmit level="2">60</transmit>
+ <transmit level="3">70</transmit>
+ <transmit level="4">80</transmit>
+ </active>
+ </test-modem>
+
+ <test-modem name="testModemPowerProfile_partiallyDefined">
+ <!-- Modem sleep drain current value in mA. -->
+ <sleep>1</sleep>
+ <!-- Modem idle drain current value in mA. -->
+ <idle>2</idle>
+ <active rat="DEFAULT">
+ <!-- Transmit current drain in mA. -->
+ <receive>3</receive>
+ <!-- Transmit current drains in mA. Must be defined for all levels (0 to 4) -->
+ <transmit level="0">4</transmit>
+ <transmit level="1">5</transmit>
+ <transmit level="2">6</transmit>
+ <transmit level="3">7</transmit>
+ <transmit level="4">8</transmit>
+ </active>
+ <active rat="NR" nrFrequency="DEFAULT">
+ <!-- Transmit current drain in mA. -->
+ <receive>13</receive>
+ <!-- Transmit current drains in mA. Must be defined for all levels (0 to 4) -->
+ <transmit level="0">14</transmit>
+ <transmit level="1">15</transmit>
+ <transmit level="2">16</transmit>
+ <transmit level="3">17</transmit>
+ <transmit level="4">18</transmit>
+ </active>
+ <active rat="NR" nrFrequency="MMWAVE">
+ <!-- Transmit current drain in mA. -->
+ <receive>53</receive>
+ <!-- Transmit current drains in mA. Must be defined for all levels (0 to 4) -->
+ <transmit level="0">54</transmit>
+ <transmit level="1">55</transmit>
+ <transmit level="2">56</transmit>
+ <transmit level="3">57</transmit>
+ <transmit level="4">58</transmit>
+ </active>
+ </test-modem>
+
+ <test-modem name="testModemPowerProfile_fullyDefined">
+ <!-- Modem sleep drain current value in mA. -->
+ <sleep>1</sleep>
+ <!-- Modem idle drain current value in mA. -->
+ <idle>2</idle>
+ <active rat="DEFAULT">
+ <!-- Transmit current drain in mA. -->
+ <receive>3</receive>
+ <!-- Transmit current drains in mA. Must be defined for all levels (0 to 4) -->
+ <transmit level="0">4</transmit>
+ <transmit level="1">5</transmit>
+ <transmit level="2">6</transmit>
+ <transmit level="3">7</transmit>
+ <transmit level="4">8</transmit>
+ </active>
+ <active rat="LTE">
+ <!-- Transmit current drain in mA. -->
+ <receive>10</receive>
+ <!-- Transmit current drains in mA. Must be defined for all levels (0 to 4) -->
+ <transmit level="0">20</transmit>
+ <transmit level="1">30</transmit>
+ <transmit level="2">40</transmit>
+ <transmit level="3">50</transmit>
+ <transmit level="4">60</transmit>
+ </active>
+ <active rat="NR" nrFrequency="DEFAULT">
+ <!-- Transmit current drain in mA. -->
+ <receive>13</receive>
+ <!-- Transmit current drains in mA. Must be defined for all levels (0 to 4) -->
+ <transmit level="0">14</transmit>
+ <transmit level="1">15</transmit>
+ <transmit level="2">16</transmit>
+ <transmit level="3">17</transmit>
+ <transmit level="4">18</transmit>
+ </active>
+ <active rat="NR" nrFrequency="LOW">
+ <!-- Transmit current drain in mA. -->
+ <receive>23</receive>
+ <!-- Transmit current drains in mA. Must be defined for all levels (0 to 4) -->
+ <transmit level="0">24</transmit>
+ <transmit level="1">25</transmit>
+ <transmit level="2">26</transmit>
+ <transmit level="3">27</transmit>
+ <transmit level="4">28</transmit>
+ </active>
+ <active rat="NR" nrFrequency="MID">
+ <!-- Transmit current drain in mA. -->
+ <receive>33</receive>
+ <!-- Transmit current drains in mA. Must be defined for all levels (0 to 4) -->
+ <transmit level="0">34</transmit>
+ <transmit level="1">35</transmit>
+ <transmit level="2">36</transmit>
+ <transmit level="3">37</transmit>
+ <transmit level="4">38</transmit>
+ </active>
+ <active rat="NR" nrFrequency="HIGH">
+ <!-- Transmit current drain in mA. -->
+ <receive>43</receive>
+ <!-- Transmit current drains in mA. Must be defined for all levels (0 to 4) -->
+ <transmit level="0">44</transmit>
+ <transmit level="1">45</transmit>
+ <transmit level="2">46</transmit>
+ <transmit level="3">47</transmit>
+ <transmit level="4">48</transmit>
+ </active>
+ <active rat="NR" nrFrequency="MMWAVE">
+ <!-- Transmit current drain in mA. -->
+ <receive>53</receive>
+ <!-- Transmit current drains in mA. Must be defined for all levels (0 to 4) -->
+ <transmit level="0">54</transmit>
+ <transmit level="1">55</transmit>
+ <transmit level="2">56</transmit>
+ <transmit level="3">57</transmit>
+ <transmit level="4">58</transmit>
+ </active>
+ </test-modem>
+</device>
diff --git a/core/tests/coretests/src/android/content/pm/CrossProfileAppsTest.java b/core/tests/coretests/src/android/content/pm/CrossProfileAppsTest.java
index 0cfcd8f85784..b66642c20808 100644
--- a/core/tests/coretests/src/android/content/pm/CrossProfileAppsTest.java
+++ b/core/tests/coretests/src/android/content/pm/CrossProfileAppsTest.java
@@ -16,11 +16,17 @@
package android.content.pm;
+import static android.app.admin.DevicePolicyResources.Strings.Core.SWITCH_TO_PERSONAL_LABEL;
+import static android.app.admin.DevicePolicyResources.Strings.Core.SWITCH_TO_WORK_LABEL;
+
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
@@ -58,6 +64,8 @@ public class CrossProfileAppsTest {
@Mock
private UserManager mUserManager;
@Mock
+ private DevicePolicyManager mDevicePolicyManager;
+ @Mock
private ICrossProfileApps mService;
@Mock
private Resources mResources;
@@ -75,6 +83,10 @@ public class CrossProfileAppsTest {
when(mContext.getPackageName()).thenReturn(MY_PACKAGE);
when(mContext.getSystemServiceName(UserManager.class)).thenReturn(Context.USER_SERVICE);
when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
+ when(mContext.getSystemServiceName(DevicePolicyManager.class)).thenReturn(
+ Context.DEVICE_POLICY_SERVICE);
+ when(mContext.getSystemService(Context.DEVICE_POLICY_SERVICE)).thenReturn(
+ mDevicePolicyManager);
}
@Before
@@ -98,7 +110,7 @@ public class CrossProfileAppsTest {
setValidTargetProfile(MANAGED_PROFILE);
mCrossProfileApps.getProfileSwitchingLabel(MANAGED_PROFILE);
- verify(mResources).getString(R.string.managed_profile_label);
+ verify(mDevicePolicyManager).getString(eq(SWITCH_TO_WORK_LABEL), any());
}
@Test
@@ -106,7 +118,7 @@ public class CrossProfileAppsTest {
setValidTargetProfile(PERSONAL_PROFILE);
mCrossProfileApps.getProfileSwitchingLabel(PERSONAL_PROFILE);
- verify(mResources).getString(R.string.user_owner_label);
+ verify(mDevicePolicyManager).getString(eq(SWITCH_TO_PERSONAL_LABEL), any());
}
@Test(expected = SecurityException.class)
diff --git a/core/tests/coretests/src/android/os/VibratorInfoTest.java b/core/tests/coretests/src/android/os/VibratorInfoTest.java
index d0e03a24427e..88766e2d97d4 100644
--- a/core/tests/coretests/src/android/os/VibratorInfoTest.java
+++ b/core/tests/coretests/src/android/os/VibratorInfoTest.java
@@ -25,7 +25,6 @@ import static org.junit.Assert.assertTrue;
import android.hardware.vibrator.Braking;
import android.hardware.vibrator.IVibrator;
import android.platform.test.annotations.Presubmit;
-import android.util.Range;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -43,10 +42,10 @@ public class VibratorInfoTest {
private static final float[] TEST_AMPLITUDE_MAP = new float[]{
/* 50Hz= */ 0.1f, 0.2f, 0.4f, 0.8f, /* 150Hz= */ 1f, 0.9f, /* 200Hz= */ 0.8f};
- private static final VibratorInfo.FrequencyMapping EMPTY_FREQUENCY_MAPPING =
- new VibratorInfo.FrequencyMapping(Float.NaN, Float.NaN, Float.NaN, null);
- private static final VibratorInfo.FrequencyMapping TEST_FREQUENCY_MAPPING =
- new VibratorInfo.FrequencyMapping(TEST_RESONANT_FREQUENCY, TEST_MIN_FREQUENCY,
+ private static final VibratorInfo.FrequencyProfile EMPTY_FREQUENCY_PROFILE =
+ new VibratorInfo.FrequencyProfile(Float.NaN, Float.NaN, Float.NaN, null);
+ private static final VibratorInfo.FrequencyProfile TEST_FREQUENCY_PROFILE =
+ new VibratorInfo.FrequencyProfile(TEST_RESONANT_FREQUENCY, TEST_MIN_FREQUENCY,
TEST_FREQUENCY_RESOLUTION, TEST_AMPLITUDE_MAP);
@Test
@@ -142,95 +141,109 @@ public class VibratorInfoTest {
}
@Test
- public void testGetFrequencyRangeHz_invalidFrequencyMappingReturnsNull() {
+ public void testGetFrequencyProfile_unsetProfileIsEmpty() {
+ assertTrue(
+ new VibratorInfo.Builder(TEST_VIBRATOR_ID).build().getFrequencyProfile().isEmpty());
+ }
+
+ @Test
+ public void testFrequencyProfile_invalidValuesCreatesEmptyProfile() {
// Invalid, contains NaN values or empty array.
- assertNull(new VibratorInfo.Builder(TEST_VIBRATOR_ID).build().getFrequencyRangeHz());
- assertNull(new VibratorInfo.Builder(TEST_VIBRATOR_ID)
- .setFrequencyMapping(new VibratorInfo.FrequencyMapping(
- Float.NaN, 50, 25, TEST_AMPLITUDE_MAP))
- .build().getFrequencyRangeHz());
- assertNull(new VibratorInfo.Builder(TEST_VIBRATOR_ID)
- .setFrequencyMapping(new VibratorInfo.FrequencyMapping(
- 150, Float.NaN, 25, TEST_AMPLITUDE_MAP))
- .build().getFrequencyRangeHz());
- assertNull(new VibratorInfo.Builder(TEST_VIBRATOR_ID)
- .setFrequencyMapping(new VibratorInfo.FrequencyMapping(
- 150, 50, Float.NaN, TEST_AMPLITUDE_MAP))
- .build().getFrequencyRangeHz());
- assertNull(new VibratorInfo.Builder(TEST_VIBRATOR_ID)
- .setFrequencyMapping(new VibratorInfo.FrequencyMapping(150, 50, 25, null))
- .build().getFrequencyRangeHz());
+ assertTrue(new VibratorInfo.FrequencyProfile(
+ Float.NaN, 50, 25, TEST_AMPLITUDE_MAP).isEmpty());
+ assertTrue(new VibratorInfo.FrequencyProfile(
+ 150, Float.NaN, 25, TEST_AMPLITUDE_MAP).isEmpty());
+ assertTrue(new VibratorInfo.FrequencyProfile(
+ 150, 50, Float.NaN, TEST_AMPLITUDE_MAP).isEmpty());
+ assertTrue(new VibratorInfo.FrequencyProfile(150, 50, 25, null).isEmpty());
+ // Invalid, contains zero or negative frequency values.
+ assertTrue(new VibratorInfo.FrequencyProfile(-1, 50, 25, TEST_AMPLITUDE_MAP).isEmpty());
+ assertTrue(new VibratorInfo.FrequencyProfile(150, 0, 25, TEST_AMPLITUDE_MAP).isEmpty());
+ assertTrue(new VibratorInfo.FrequencyProfile(150, 50, -2, TEST_AMPLITUDE_MAP).isEmpty());
+ // Invalid max amplitude entries.
+ assertTrue(new VibratorInfo.FrequencyProfile(
+ 150, 50, 50, new float[] { -1, 0, 1, 1, 0 }).isEmpty());
+ assertTrue(new VibratorInfo.FrequencyProfile(
+ 150, 50, 50, new float[] { 0, 1, 2, 1, 0 }).isEmpty());
// Invalid, minFrequency > resonantFrequency
- assertNull(new VibratorInfo.Builder(TEST_VIBRATOR_ID)
- .setFrequencyMapping(new VibratorInfo.FrequencyMapping(
- /* resonantFrequencyHz= */ 150, /* minFrequencyHz= */ 250, 25, null))
- .build().getFrequencyRangeHz());
+ assertTrue(new VibratorInfo.FrequencyProfile(
+ /* resonantFrequencyHz= */ 150, /* minFrequencyHz= */ 250, 25, TEST_AMPLITUDE_MAP)
+ .isEmpty());
// Invalid, maxFrequency < resonantFrequency by changing resolution.
- assertNull(new VibratorInfo.Builder(TEST_VIBRATOR_ID)
- .setFrequencyMapping(new VibratorInfo.FrequencyMapping(
- 150, 50, /* frequencyResolutionHz= */ 10, null))
- .build().getFrequencyRangeHz());
+ assertTrue(new VibratorInfo.FrequencyProfile(
+ 150, 50, /* frequencyResolutionHz= */ 10, TEST_AMPLITUDE_MAP).isEmpty());
}
@Test
- public void testGetFrequencyRangeHz_resultRangeDerivedFromHalMapping() {
- VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
- .setFrequencyMapping(new VibratorInfo.FrequencyMapping(
- /* resonantFrequencyHz= */ 150,
- /* minFrequencyHz= */ 50,
- /* frequencyResolutionHz= */ 25,
- new float[]{
- /* 50Hz= */ 0.1f, 0.2f, 0.4f, 0.8f, /* 150Hz= */ 1f, 0.9f,
- /* 200Hz= */ 0.8f}))
- .build();
+ public void testGetFrequencyRangeHz_emptyProfileReturnsNull() {
+ assertNull(new VibratorInfo.FrequencyProfile(
+ Float.NaN, 50, 25, TEST_AMPLITUDE_MAP).getFrequencyRangeHz());
+ assertNull(new VibratorInfo.FrequencyProfile(
+ 150, Float.NaN, 25, TEST_AMPLITUDE_MAP).getFrequencyRangeHz());
+ assertNull(new VibratorInfo.FrequencyProfile(
+ 150, 50, Float.NaN, TEST_AMPLITUDE_MAP).getFrequencyRangeHz());
+ assertNull(new VibratorInfo.FrequencyProfile(150, 50, 25, null).getFrequencyRangeHz());
+ }
- assertEquals(Range.create(50f, 200f), info.getFrequencyRangeHz());
+ @Test
+ public void testGetFrequencyRangeHz_validProfileReturnsMappedValues() {
+ VibratorInfo.FrequencyProfile profile = new VibratorInfo.FrequencyProfile(
+ /* resonantFrequencyHz= */ 150,
+ /* minFrequencyHz= */ 50,
+ /* frequencyResolutionHz= */ 25,
+ /* maxAmplitudes= */ new float[]{
+ /* 50Hz= */ 0.1f, 0.2f, 0.4f, 0.8f, /* 150Hz= */ 1f, 0.9f,
+ /* 200Hz= */ 0.8f});
+
+ assertEquals(50f, profile.getFrequencyRangeHz().getLower(), TEST_TOLERANCE);
+ assertEquals(200f, profile.getFrequencyRangeHz().getUpper(), TEST_TOLERANCE);
}
@Test
- public void testGetMaxAmplitude_emptyMappingReturnsAlwaysZero() {
- VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID).build();
- assertEquals(0f, info.getMaxAmplitude(Float.NaN), TEST_TOLERANCE);
- assertEquals(0f, info.getMaxAmplitude(100f), TEST_TOLERANCE);
- assertEquals(0f, info.getMaxAmplitude(200f), TEST_TOLERANCE);
+ public void testGetMaxAmplitude_emptyProfileReturnsAlwaysZero() {
+ VibratorInfo.FrequencyProfile profile = EMPTY_FREQUENCY_PROFILE;
+ assertEquals(0f, profile.getMaxAmplitude(Float.NaN), TEST_TOLERANCE);
+ assertEquals(0f, profile.getMaxAmplitude(100f), TEST_TOLERANCE);
+ assertEquals(0f, profile.getMaxAmplitude(200f), TEST_TOLERANCE);
- info = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
- .setFrequencyMapping(new VibratorInfo.FrequencyMapping(
+ profile = new VibratorInfo.FrequencyProfile(
/* resonantFrequencyHz= */ 150,
/* minFrequencyHz= */ Float.NaN,
/* frequencyResolutionHz= */ Float.NaN,
- null))
- .build();
+ /* maxAmplitudes= */ null);
- assertEquals(0f, info.getMaxAmplitude(Float.NaN), TEST_TOLERANCE);
- assertEquals(0f, info.getMaxAmplitude(100f), TEST_TOLERANCE);
- assertEquals(0f, info.getMaxAmplitude(150f), TEST_TOLERANCE);
+ assertEquals(0f, profile.getMaxAmplitude(Float.NaN), TEST_TOLERANCE);
+ assertEquals(0f, profile.getMaxAmplitude(100f), TEST_TOLERANCE);
+ assertEquals(0f, profile.getMaxAmplitude(150f), TEST_TOLERANCE);
}
@Test
- public void testGetMaxAmplitude_validMappingReturnsMappedValues() {
- VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
- .setFrequencyMapping(new VibratorInfo.FrequencyMapping(
+ public void testGetMaxAmplitude_validProfileReturnsMappedValues() {
+ VibratorInfo.FrequencyProfile profile = new VibratorInfo.FrequencyProfile(
/* resonantFrequencyHz= */ 150,
/* minFrequencyHz= */ 50,
/* frequencyResolutionHz= */ 25,
- new float[]{
+ /* maxAmplitudes= */ new float[]{
/* 50Hz= */ 0.1f, 0.2f, 0.4f, 0.8f, /* 150Hz= */ 1f, 0.9f,
- /* 200Hz= */ 0.8f}))
- .build();
+ /* 200Hz= */ 0.8f});
- assertEquals(1f, info.getMaxAmplitude(150f), TEST_TOLERANCE);
- assertEquals(0.9f, info.getMaxAmplitude(175f), TEST_TOLERANCE);
- assertEquals(0.8f, info.getMaxAmplitude(125f), TEST_TOLERANCE);
- assertEquals(0.8f, info.getMaxAmplitude(info.getFrequencyRangeHz().getUpper()),
- TEST_TOLERANCE); // 200Hz
- assertEquals(0.1f, info.getMaxAmplitude(info.getFrequencyRangeHz().getLower()),
- TEST_TOLERANCE); // 50Hz
-
- // 145Hz maps to the max amplitude for 125Hz, which is lower.
- assertEquals(0.8f, info.getMaxAmplitude(145f), TEST_TOLERANCE); // 145Hz
- // 185Hz maps to the max amplitude for 200Hz, which is lower.
- assertEquals(0.8f, info.getMaxAmplitude(185f), TEST_TOLERANCE); // 185Hz
+ // Values in the max amplitudes array should return exact measurement.
+ assertEquals(1f, profile.getMaxAmplitude(150f), TEST_TOLERANCE);
+ assertEquals(0.9f, profile.getMaxAmplitude(175f), TEST_TOLERANCE);
+ assertEquals(0.8f, profile.getMaxAmplitude(125f), TEST_TOLERANCE);
+
+ // Min and max frequencies should return exact measurement from array.
+ assertEquals(0.8f, profile.getMaxAmplitude(200f), TEST_TOLERANCE);
+ assertEquals(0.1f, profile.getMaxAmplitude(50f), TEST_TOLERANCE);
+
+ // Values outside [50Hz, 200Hz] just return 0.
+ assertEquals(0f, profile.getMaxAmplitude(49f), TEST_TOLERANCE);
+ assertEquals(0f, profile.getMaxAmplitude(201f), TEST_TOLERANCE);
+
+ // 145Hz maps to linear value between 125Hz and 150Hz max amplitudes 0.8 and 1.
+ assertEquals(0.96f, profile.getMaxAmplitude(145f), TEST_TOLERANCE);
+ // 185Hz maps to linear value between 175Hz and 200Hz max amplitudes 0.9 and 0.8.
+ assertEquals(0.86f, profile.getMaxAmplitude(185f), TEST_TOLERANCE);
}
@Test
@@ -245,7 +258,7 @@ public class VibratorInfoTest {
.setPwlePrimitiveDurationMax(50)
.setPwleSizeMax(20)
.setQFactor(2f)
- .setFrequencyMapping(TEST_FREQUENCY_MAPPING);
+ .setFrequencyProfile(TEST_FREQUENCY_PROFILE);
VibratorInfo complete = completeBuilder.build();
assertEquals(complete, complete);
@@ -272,23 +285,21 @@ public class VibratorInfoTest {
.build();
assertNotEquals(complete, completeWithDifferentPrimitiveDuration);
- VibratorInfo completeWithDifferentFrequencyMapping = completeBuilder
- .setFrequencyMapping(new VibratorInfo.FrequencyMapping(
+ VibratorInfo completeWithDifferentFrequencyProfile = completeBuilder
+ .setFrequencyProfile(new VibratorInfo.FrequencyProfile(
TEST_RESONANT_FREQUENCY + 20,
TEST_MIN_FREQUENCY + 10,
TEST_FREQUENCY_RESOLUTION + 5,
TEST_AMPLITUDE_MAP))
.build();
- assertNotEquals(complete, completeWithDifferentFrequencyMapping);
+ assertNotEquals(complete, completeWithDifferentFrequencyProfile);
- VibratorInfo completeWithEmptyFrequencyMapping = completeBuilder
- .setFrequencyMapping(EMPTY_FREQUENCY_MAPPING)
+ VibratorInfo completeWithEmptyFrequencyProfile = completeBuilder
+ .setFrequencyProfile(EMPTY_FREQUENCY_PROFILE)
.build();
- assertNotEquals(complete, completeWithEmptyFrequencyMapping);
+ assertNotEquals(complete, completeWithEmptyFrequencyProfile);
- VibratorInfo completeWithUnknownQFactor = completeBuilder
- .setQFactor(Float.NaN)
- .build();
+ VibratorInfo completeWithUnknownQFactor = completeBuilder.setQFactor(Float.NaN).build();
assertNotEquals(complete, completeWithUnknownQFactor);
VibratorInfo completeWithDifferentQFactor = completeBuilder
@@ -316,7 +327,7 @@ public class VibratorInfoTest {
.setSupportedEffects(VibrationEffect.EFFECT_CLICK)
.setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 20)
.setQFactor(Float.NaN)
- .setFrequencyMapping(TEST_FREQUENCY_MAPPING)
+ .setFrequencyProfile(TEST_FREQUENCY_PROFILE)
.build();
Parcel parcel = Parcel.obtain();
diff --git a/core/tests/coretests/src/android/os/VibratorTest.java b/core/tests/coretests/src/android/os/VibratorTest.java
index 981086d6b152..7a66befad1a1 100644
--- a/core/tests/coretests/src/android/os/VibratorTest.java
+++ b/core/tests/coretests/src/android/os/VibratorTest.java
@@ -61,6 +61,8 @@ public class VibratorTest {
@Rule
public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
+ private static final float TEST_TOLERANCE = 1e-5f;
+
private Context mContextSpy;
private Vibrator mVibratorSpy;
@@ -76,6 +78,9 @@ public class VibratorTest {
@Test
public void getId_returnsDefaultId() {
assertEquals(-1, mVibratorSpy.getId());
+ assertEquals(-1, new SystemVibrator.NoVibratorInfo().getId());
+ assertEquals(-1, new SystemVibrator.MultiVibratorInfo(new VibratorInfo[] {
+ VibratorInfo.EMPTY_VIBRATOR_INFO, VibratorInfo.EMPTY_VIBRATOR_INFO }).getId());
}
@Test
@@ -90,8 +95,7 @@ public class VibratorTest {
@Test
public void areEffectsSupported_noVibrator_returnsAlwaysNo() {
- SystemVibrator.AllVibratorsInfo info = new SystemVibrator.AllVibratorsInfo(
- new VibratorInfo[0]);
+ VibratorInfo info = new SystemVibrator.NoVibratorInfo();
assertEquals(Vibrator.VIBRATION_EFFECT_SUPPORT_NO,
info.isEffectSupported(VibrationEffect.EFFECT_CLICK));
}
@@ -104,7 +108,7 @@ public class VibratorTest {
VibratorInfo unsupportedVibrator = new VibratorInfo.Builder(/* id= */ 2)
.setSupportedEffects(new int[0])
.build();
- SystemVibrator.AllVibratorsInfo info = new SystemVibrator.AllVibratorsInfo(
+ VibratorInfo info = new SystemVibrator.MultiVibratorInfo(
new VibratorInfo[]{supportedVibrator, unsupportedVibrator});
assertEquals(Vibrator.VIBRATION_EFFECT_SUPPORT_NO,
info.isEffectSupported(VibrationEffect.EFFECT_CLICK));
@@ -116,7 +120,7 @@ public class VibratorTest {
.setSupportedEffects(VibrationEffect.EFFECT_CLICK)
.build();
VibratorInfo unknownSupportVibrator = VibratorInfo.EMPTY_VIBRATOR_INFO;
- SystemVibrator.AllVibratorsInfo info = new SystemVibrator.AllVibratorsInfo(
+ VibratorInfo info = new SystemVibrator.MultiVibratorInfo(
new VibratorInfo[]{supportedVibrator, unknownSupportVibrator});
assertEquals(Vibrator.VIBRATION_EFFECT_SUPPORT_UNKNOWN,
info.isEffectSupported(VibrationEffect.EFFECT_CLICK));
@@ -130,7 +134,7 @@ public class VibratorTest {
VibratorInfo secondVibrator = new VibratorInfo.Builder(/* id= */ 2)
.setSupportedEffects(VibrationEffect.EFFECT_CLICK)
.build();
- SystemVibrator.AllVibratorsInfo info = new SystemVibrator.AllVibratorsInfo(
+ VibratorInfo info = new SystemVibrator.MultiVibratorInfo(
new VibratorInfo[]{firstVibrator, secondVibrator});
assertEquals(Vibrator.VIBRATION_EFFECT_SUPPORT_YES,
info.isEffectSupported(VibrationEffect.EFFECT_CLICK));
@@ -148,8 +152,7 @@ public class VibratorTest {
@Test
public void arePrimitivesSupported_noVibrator_returnsAlwaysFalse() {
- SystemVibrator.AllVibratorsInfo info = new SystemVibrator.AllVibratorsInfo(
- new VibratorInfo[0]);
+ VibratorInfo info = new SystemVibrator.NoVibratorInfo();
assertFalse(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_CLICK));
}
@@ -160,7 +163,7 @@ public class VibratorTest {
.setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
.build();
VibratorInfo unsupportedVibrator = VibratorInfo.EMPTY_VIBRATOR_INFO;
- SystemVibrator.AllVibratorsInfo info = new SystemVibrator.AllVibratorsInfo(
+ VibratorInfo info = new SystemVibrator.MultiVibratorInfo(
new VibratorInfo[]{supportedVibrator, unsupportedVibrator});
assertFalse(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_CLICK));
}
@@ -175,7 +178,7 @@ public class VibratorTest {
.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
.setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 15)
.build();
- SystemVibrator.AllVibratorsInfo info = new SystemVibrator.AllVibratorsInfo(
+ VibratorInfo info = new SystemVibrator.MultiVibratorInfo(
new VibratorInfo[]{firstVibrator, secondVibrator});
assertTrue(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_CLICK));
}
@@ -192,8 +195,7 @@ public class VibratorTest {
@Test
public void getPrimitivesDurations_noVibrator_returnsAlwaysZero() {
- SystemVibrator.AllVibratorsInfo info = new SystemVibrator.AllVibratorsInfo(
- new VibratorInfo[0]);
+ VibratorInfo info = new SystemVibrator.NoVibratorInfo();
assertEquals(0, info.getPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_CLICK));
}
@@ -204,7 +206,7 @@ public class VibratorTest {
.setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
.build();
VibratorInfo unsupportedVibrator = VibratorInfo.EMPTY_VIBRATOR_INFO;
- SystemVibrator.AllVibratorsInfo info = new SystemVibrator.AllVibratorsInfo(
+ VibratorInfo info = new SystemVibrator.MultiVibratorInfo(
new VibratorInfo[]{supportedVibrator, unsupportedVibrator});
assertEquals(0, info.getPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_CLICK));
}
@@ -219,12 +221,180 @@ public class VibratorTest {
.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
.setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 20)
.build();
- SystemVibrator.AllVibratorsInfo info = new SystemVibrator.AllVibratorsInfo(
+ VibratorInfo info = new SystemVibrator.MultiVibratorInfo(
new VibratorInfo[]{firstVibrator, secondVibrator});
assertEquals(20, info.getPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_CLICK));
}
@Test
+ public void getQFactorAndResonantFrequency_noVibrator_returnsNaN() {
+ VibratorInfo info = new SystemVibrator.NoVibratorInfo();
+
+ assertTrue(Float.isNaN(info.getQFactor()));
+ assertTrue(Float.isNaN(info.getResonantFrequencyHz()));
+ }
+
+ @Test
+ public void getQFactorAndResonantFrequency_differentValues_returnsNaN() {
+ VibratorInfo firstVibrator = new VibratorInfo.Builder(/* id= */ 1)
+ .setQFactor(1f)
+ .setFrequencyProfile(new VibratorInfo.FrequencyProfile(1, 1, 1, null))
+ .build();
+ VibratorInfo secondVibrator = new VibratorInfo.Builder(/* id= */ 2)
+ .setQFactor(2f)
+ .setFrequencyProfile(new VibratorInfo.FrequencyProfile(2, 2, 2, null))
+ .build();
+ VibratorInfo info = new SystemVibrator.MultiVibratorInfo(
+ new VibratorInfo[]{firstVibrator, secondVibrator});
+
+ assertTrue(Float.isNaN(info.getQFactor()));
+ assertTrue(Float.isNaN(info.getResonantFrequencyHz()));
+
+ // One vibrator with values undefined.
+ VibratorInfo thirdVibrator = new VibratorInfo.Builder(/* id= */ 3).build();
+ info = new SystemVibrator.MultiVibratorInfo(
+ new VibratorInfo[]{firstVibrator, thirdVibrator});
+
+ assertTrue(Float.isNaN(info.getQFactor()));
+ assertTrue(Float.isNaN(info.getResonantFrequencyHz()));
+ }
+
+ @Test
+ public void getQFactorAndResonantFrequency_sameValues_returnsValue() {
+ VibratorInfo firstVibrator = new VibratorInfo.Builder(/* id= */ 1)
+ .setQFactor(10f)
+ .setFrequencyProfile(new VibratorInfo.FrequencyProfile(
+ /* resonantFrequencyHz= */ 11, 10, 0.5f, null))
+ .build();
+ VibratorInfo secondVibrator = new VibratorInfo.Builder(/* id= */ 2)
+ .setQFactor(10f)
+ .setFrequencyProfile(new VibratorInfo.FrequencyProfile(
+ /* resonantFrequencyHz= */ 11, 5, 1, null))
+ .build();
+ VibratorInfo info = new SystemVibrator.MultiVibratorInfo(
+ new VibratorInfo[]{firstVibrator, secondVibrator});
+
+ assertEquals(10f, info.getQFactor(), TEST_TOLERANCE);
+ assertEquals(11f, info.getResonantFrequencyHz(), TEST_TOLERANCE);
+ }
+
+ @Test
+ public void getFrequencyProfile_noVibrator_returnsEmpty() {
+ VibratorInfo info = new SystemVibrator.NoVibratorInfo();
+
+ assertTrue(info.getFrequencyProfile().isEmpty());
+ }
+
+ @Test
+ public void getFrequencyProfile_differentResonantFrequencyOrResolutionValues_returnsEmpty() {
+ VibratorInfo firstVibrator = new VibratorInfo.Builder(/* id= */ 1)
+ .setFrequencyProfile(new VibratorInfo.FrequencyProfile(1, 1, 1,
+ new float[] { 0, 1 }))
+ .build();
+ VibratorInfo differentResonantFrequency = new VibratorInfo.Builder(/* id= */ 2)
+ .setFrequencyProfile(new VibratorInfo.FrequencyProfile(2, 1, 1,
+ new float[] { 0, 1 }))
+ .build();
+ VibratorInfo info = new SystemVibrator.MultiVibratorInfo(
+ new VibratorInfo[]{firstVibrator, differentResonantFrequency});
+
+ assertTrue(info.getFrequencyProfile().isEmpty());
+
+ VibratorInfo differentFrequencyResolution = new VibratorInfo.Builder(/* id= */ 2)
+ .setFrequencyProfile(new VibratorInfo.FrequencyProfile(1, 1, 2,
+ new float[] { 0, 1 }))
+ .build();
+ info = new SystemVibrator.MultiVibratorInfo(
+ new VibratorInfo[]{firstVibrator, differentFrequencyResolution});
+
+ assertTrue(info.getFrequencyProfile().isEmpty());
+ }
+
+ @Test
+ public void getFrequencyProfile_missingValues_returnsEmpty() {
+ VibratorInfo firstVibrator = new VibratorInfo.Builder(/* id= */ 1)
+ .setFrequencyProfile(new VibratorInfo.FrequencyProfile(1, 1, 1,
+ new float[] { 0, 1 }))
+ .build();
+ VibratorInfo missingResonantFrequency = new VibratorInfo.Builder(/* id= */ 2)
+ .setFrequencyProfile(new VibratorInfo.FrequencyProfile(Float.NaN, 1, 1,
+ new float[] { 0, 1 }))
+ .build();
+ VibratorInfo info = new SystemVibrator.MultiVibratorInfo(
+ new VibratorInfo[]{firstVibrator, missingResonantFrequency});
+
+ assertTrue(info.getFrequencyProfile().isEmpty());
+
+ VibratorInfo missingMinFrequency = new VibratorInfo.Builder(/* id= */ 2)
+ .setFrequencyProfile(new VibratorInfo.FrequencyProfile(1, Float.NaN, 1,
+ new float[] { 0, 1 }))
+ .build();
+ info = new SystemVibrator.MultiVibratorInfo(
+ new VibratorInfo[]{firstVibrator, missingMinFrequency});
+
+ assertTrue(info.getFrequencyProfile().isEmpty());
+
+ VibratorInfo missingFrequencyResolution = new VibratorInfo.Builder(/* id= */ 2)
+ .setFrequencyProfile(new VibratorInfo.FrequencyProfile(1, 1, Float.NaN,
+ new float[] { 0, 1 }))
+ .build();
+ info = new SystemVibrator.MultiVibratorInfo(
+ new VibratorInfo[]{firstVibrator, missingFrequencyResolution});
+
+ assertTrue(info.getFrequencyProfile().isEmpty());
+
+ VibratorInfo missingMaxAmplitudes = new VibratorInfo.Builder(/* id= */ 2)
+ .setFrequencyProfile(new VibratorInfo.FrequencyProfile(1, 1, 1, null))
+ .build();
+ info = new SystemVibrator.MultiVibratorInfo(
+ new VibratorInfo[]{firstVibrator, missingMaxAmplitudes});
+
+ assertTrue(info.getFrequencyProfile().isEmpty());
+ }
+
+ @Test
+ public void getFrequencyProfile_unalignedMaxAmplitudes_returnsEmpty() {
+ VibratorInfo firstVibrator = new VibratorInfo.Builder(/* id= */ 1)
+ .setFrequencyProfile(new VibratorInfo.FrequencyProfile(11, 10, 0.5f,
+ new float[] { 0, 1, 1, 0 }))
+ .build();
+ VibratorInfo unalignedMinFrequency = new VibratorInfo.Builder(/* id= */ 2)
+ .setFrequencyProfile(new VibratorInfo.FrequencyProfile(11, 10.1f, 0.5f,
+ new float[] { 0, 1, 1, 0 }))
+ .build();
+ VibratorInfo thirdVibrator = new VibratorInfo.Builder(/* id= */ 2)
+ .setFrequencyProfile(new VibratorInfo.FrequencyProfile(11, 10.5f, 0.5f,
+ new float[] { 0, 1, 1, 0 }))
+ .build();
+ VibratorInfo info = new SystemVibrator.MultiVibratorInfo(
+ new VibratorInfo[]{firstVibrator, unalignedMinFrequency, thirdVibrator});
+
+ assertTrue(info.getFrequencyProfile().isEmpty());
+ }
+
+ @Test
+ public void getFrequencyProfile_alignedProfiles_returnsIntersection() {
+ VibratorInfo firstVibrator = new VibratorInfo.Builder(/* id= */ 1)
+ .setFrequencyProfile(new VibratorInfo.FrequencyProfile(11, 10, 0.5f,
+ new float[] { 0.5f, 1, 1, 0.5f }))
+ .build();
+ VibratorInfo secondVibrator = new VibratorInfo.Builder(/* id= */ 2)
+ .setFrequencyProfile(new VibratorInfo.FrequencyProfile(11, 10.5f, 0.5f,
+ new float[] { 1, 1, 1 }))
+ .build();
+ VibratorInfo thirdVibrator = new VibratorInfo.Builder(/* id= */ 3)
+ .setFrequencyProfile(new VibratorInfo.FrequencyProfile(11, 10.5f, 0.5f,
+ new float[] { 0.8f, 1, 0.8f, 0.5f }))
+ .build();
+ VibratorInfo info = new SystemVibrator.MultiVibratorInfo(
+ new VibratorInfo[]{firstVibrator, secondVibrator, thirdVibrator});
+
+ assertEquals(
+ new VibratorInfo.FrequencyProfile(11, 10.5f, 0.5f, new float[] { 0.8f, 1, 0.5f }),
+ info.getFrequencyProfile());
+ }
+
+ @Test
public void vibrate_withVibrationAttributes_usesGivenAttributes() {
VibrationEffect effect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
VibrationAttributes attributes = new VibrationAttributes.Builder().setUsage(
diff --git a/core/tests/coretests/src/android/util/SparseDoubleArrayTest.java b/core/tests/coretests/src/android/util/SparseDoubleArrayTest.java
index 2dd3f69852c1..ba9c8d92e173 100644
--- a/core/tests/coretests/src/android/util/SparseDoubleArrayTest.java
+++ b/core/tests/coretests/src/android/util/SparseDoubleArrayTest.java
@@ -64,12 +64,12 @@ public class SparseDoubleArrayTest {
}
@Test
- public void testAdd() {
+ public void testIncrementValue() {
final SparseDoubleArray sda = new SparseDoubleArray();
sda.put(4, 6.1);
- sda.add(4, -1.2);
- sda.add(2, -1.2);
+ sda.incrementValue(4, -1.2);
+ sda.incrementValue(2, -1.2);
assertEquals(6.1 - 1.2, sda.get(4), PRECISION);
assertEquals(-1.2, sda.get(2), PRECISION);
diff --git a/core/tests/coretests/src/android/util/SparseLongArrayTest.java b/core/tests/coretests/src/android/util/SparseLongArrayTest.java
index df2d752e04b9..b29b6f1f8e9d 100644
--- a/core/tests/coretests/src/android/util/SparseLongArrayTest.java
+++ b/core/tests/coretests/src/android/util/SparseLongArrayTest.java
@@ -154,4 +154,16 @@ public class SparseLongArrayTest {
assertRemoved(startIndex, endIndex);
assertTrue(isSame(sparseLongArray2, mSparseLongArray));
}
+
+ @Test
+ public void testIncrementValue() {
+ final SparseLongArray sla = new SparseLongArray();
+
+ sla.put(4, 6);
+ sla.incrementValue(4, 4);
+ sla.incrementValue(2, 5);
+
+ assertEquals(6 + 4, sla.get(4));
+ assertEquals(5, sla.get(2));
+ }
}
diff --git a/core/tests/coretests/src/android/view/HandwritingInitiatorTest.java b/core/tests/coretests/src/android/view/HandwritingInitiatorTest.java
index 8f044616e323..5ea91997b1f5 100644
--- a/core/tests/coretests/src/android/view/HandwritingInitiatorTest.java
+++ b/core/tests/coretests/src/android/view/HandwritingInitiatorTest.java
@@ -33,7 +33,6 @@ import android.app.Instrumentation;
import android.content.Context;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
-import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -57,7 +56,6 @@ public class HandwritingInitiatorTest {
private static final int TOUCH_SLOP = 8;
private static final long TAP_TIMEOUT = ViewConfiguration.getTapTimeout();
private static final Rect sHwArea = new Rect(100, 200, 500, 500);
- private static final EditorInfo sFakeEditorInfo = new EditorInfo();
private HandwritingInitiator mHandwritingInitiator;
private View mTestView;
@@ -72,7 +70,6 @@ public class HandwritingInitiatorTest {
InputMethodManager inputMethodManager = context.getSystemService(InputMethodManager.class);
mHandwritingInitiator =
spy(new HandwritingInitiator(viewConfiguration, inputMethodManager));
- mHandwritingInitiator.updateEditorBound(sHwArea);
// mock a parent so that HandwritingInitiator can get
ViewGroup parent = new ViewGroup(context) {
@@ -82,10 +79,7 @@ public class HandwritingInitiatorTest {
}
@Override
public boolean getChildVisibleRect(View child, Rect r, android.graphics.Point offset) {
- r.left = sHwArea.left;
- r.top = sHwArea.top;
- r.right = sHwArea.right;
- r.bottom = sHwArea.bottom;
+ r.set(sHwArea);
return true;
}
};
@@ -97,7 +91,7 @@ public class HandwritingInitiatorTest {
@Test
public void onTouchEvent_startHandwriting_when_stylusMoveOnce_withinHWArea() {
- mHandwritingInitiator.onInputConnectionCreated(mTestView, sFakeEditorInfo);
+ mHandwritingInitiator.onInputConnectionCreated(mTestView);
final int x1 = (sHwArea.left + sHwArea.right) / 2;
final int y1 = (sHwArea.top + sHwArea.bottom) / 2;
MotionEvent stylusEvent1 = createStylusEvent(ACTION_DOWN, x1, y1, 0);
@@ -109,13 +103,13 @@ public class HandwritingInitiatorTest {
MotionEvent stylusEvent2 = createStylusEvent(ACTION_MOVE, x2, y2, 0);
mHandwritingInitiator.onTouchEvent(stylusEvent2);
- // Stylus movement win HandwritingArea should trigger IMM.startHandwriting once.
+ // Stylus movement within HandwritingArea should trigger IMM.startHandwriting once.
verify(mHandwritingInitiator, times(1)).startHandwriting(mTestView);
}
@Test
public void onTouchEvent_startHandwritingOnce_when_stylusMoveMultiTimes_withinHWArea() {
- mHandwritingInitiator.onInputConnectionCreated(mTestView, sFakeEditorInfo);
+ mHandwritingInitiator.onInputConnectionCreated(mTestView);
final int x1 = (sHwArea.left + sHwArea.right) / 2;
final int y1 = (sHwArea.top + sHwArea.bottom) / 2;
MotionEvent stylusEvent1 = createStylusEvent(ACTION_DOWN, x1, y1, 0);
@@ -152,14 +146,14 @@ public class HandwritingInitiatorTest {
mHandwritingInitiator.onTouchEvent(stylusEvent2);
// InputConnection is created after stylus movement.
- mHandwritingInitiator.onInputConnectionCreated(mTestView, sFakeEditorInfo);
+ mHandwritingInitiator.onInputConnectionCreated(mTestView);
verify(mHandwritingInitiator, times(1)).startHandwriting(mTestView);
}
@Test
public void onTouchEvent_notStartHandwriting_when_stylusTap_withinHWArea() {
- mHandwritingInitiator.onInputConnectionCreated(mTestView, sFakeEditorInfo);
+ mHandwritingInitiator.onInputConnectionCreated(mTestView);
final int x1 = 200;
final int y1 = 200;
MotionEvent stylusEvent1 = createStylusEvent(ACTION_DOWN, x1, y1, 0);
@@ -175,7 +169,7 @@ public class HandwritingInitiatorTest {
@Test
public void onTouchEvent_notStartHandwriting_when_stylusMove_outOfHWArea() {
- mHandwritingInitiator.onInputConnectionCreated(mTestView, sFakeEditorInfo);
+ mHandwritingInitiator.onInputConnectionCreated(mTestView);
final int x1 = 10;
final int y1 = 10;
MotionEvent stylusEvent1 = createStylusEvent(ACTION_DOWN, x1, y1, 0);
@@ -191,7 +185,7 @@ public class HandwritingInitiatorTest {
@Test
public void onTouchEvent_notStartHandwriting_when_stylusMove_afterTapTimeOut() {
- mHandwritingInitiator.onInputConnectionCreated(mTestView, sFakeEditorInfo);
+ mHandwritingInitiator.onInputConnectionCreated(mTestView);
final int x1 = 10;
final int y1 = 10;
final long time1 = 10L;
@@ -210,18 +204,17 @@ public class HandwritingInitiatorTest {
@Test
public void onInputConnectionCreated_inputConnectionCreated() {
- mHandwritingInitiator.onInputConnectionCreated(mTestView, sFakeEditorInfo);
+ mHandwritingInitiator.onInputConnectionCreated(mTestView);
assertThat(mHandwritingInitiator.mConnectedView).isNotNull();
assertThat(mHandwritingInitiator.mConnectedView.get()).isEqualTo(mTestView);
}
@Test
public void onInputConnectionCreated_inputConnectionClosed() {
- mHandwritingInitiator.onInputConnectionCreated(mTestView, sFakeEditorInfo);
+ mHandwritingInitiator.onInputConnectionCreated(mTestView);
mHandwritingInitiator.onInputConnectionClosed(mTestView);
assertThat(mHandwritingInitiator.mConnectedView).isNull();
- assertThat(mHandwritingInitiator.mEditorBound).isNull();
}
@Test
@@ -229,22 +222,14 @@ public class HandwritingInitiatorTest {
// When IMM restarts input connection, View#onInputConnectionCreatedInternal might be
// called before View#onInputConnectionClosedInternal. As a result, we need to handle the
// case where "one view "2 InputConnections".
- mHandwritingInitiator.onInputConnectionCreated(mTestView, sFakeEditorInfo);
- mHandwritingInitiator.onInputConnectionCreated(mTestView, sFakeEditorInfo);
+ mHandwritingInitiator.onInputConnectionCreated(mTestView);
+ mHandwritingInitiator.onInputConnectionCreated(mTestView);
mHandwritingInitiator.onInputConnectionClosed(mTestView);
assertThat(mHandwritingInitiator.mConnectedView).isNotNull();
assertThat(mHandwritingInitiator.mConnectedView.get()).isEqualTo(mTestView);
}
- @Test
- public void updateEditorBound() {
- Rect rect = new Rect(1, 2, 3, 4);
- mHandwritingInitiator.updateEditorBound(rect);
-
- assertThat(mHandwritingInitiator.mEditorBound).isEqualTo(rect);
- }
-
private MotionEvent createStylusEvent(int action, int x, int y, long eventTime) {
MotionEvent.PointerProperties[] properties = MotionEvent.PointerProperties.createArray(1);
properties[0].toolType = MotionEvent.TOOL_TYPE_STYLUS;
diff --git a/core/tests/coretests/src/android/view/SurfaceControlFpsListenerTest.java b/core/tests/coretests/src/android/view/SurfaceControlFpsListenerTest.java
deleted file mode 100644
index c15fc3a15112..000000000000
--- a/core/tests/coretests/src/android/view/SurfaceControlFpsListenerTest.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * 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 android.view;
-
-import android.platform.test.annotations.Presubmit;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-@Presubmit
-public class SurfaceControlFpsListenerTest {
-
- @Test
- public void registersAndUnregisters() {
-
- SurfaceControlFpsListener listener = new SurfaceControlFpsListener() {
- @Override
- public void onFpsReported(float fps) {
- // Ignore
- }
- };
-
- listener.register(0);
-
- listener.unregister();
- }
-}
diff --git a/core/tests/coretests/src/android/window/TaskFpsCallbackTest.java b/core/tests/coretests/src/android/window/TaskFpsCallbackTest.java
new file mode 100644
index 000000000000..bf508db56852
--- /dev/null
+++ b/core/tests/coretests/src/android/window/TaskFpsCallbackTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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 org.junit.Assert.assertEquals;
+
+import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
+import android.content.Context;
+import android.platform.test.annotations.Presubmit;
+import android.view.WindowManager;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public class TaskFpsCallbackTest {
+
+ private Context mContext;
+ private WindowManager mWindowManager;
+ private ActivityTaskManager mActivityTaskManager;
+
+ @Before
+ public void setup() {
+ mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ mActivityTaskManager = mContext.getSystemService(ActivityTaskManager.class);
+ mWindowManager = mContext.getSystemService(WindowManager.class);
+ }
+
+ @Test
+ public void testRegisterAndUnregister() {
+
+ final TaskFpsCallback.OnFpsCallbackListener listener = fps -> {
+ // Ignore
+ };
+ final TaskFpsCallback callback = new TaskFpsCallback(Runnable::run, listener);
+
+ final List<ActivityManager.RunningTaskInfo> tasks = mActivityTaskManager.getTasks(1);
+ assertEquals(tasks.size(), 1);
+ mWindowManager.registerTaskFpsCallback(tasks.get(0).taskId, callback);
+ mWindowManager.unregisterTaskFpsCallback(callback);
+ }
+}
diff --git a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
new file mode 100644
index 000000000000..a1a1e20d6982
--- /dev/null
+++ b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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 org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
+
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.view.IWindow;
+import android.view.IWindowSession;
+import android.view.OnBackInvokedCallback;
+import android.view.OnBackInvokedDispatcher;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link WindowOnBackInvokedDispatcherTest}
+ *
+ * <p>Build/Install/Run:
+ * atest FrameworksCoreTests:WindowOnBackInvokedDispatcherTest
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public class WindowOnBackInvokedDispatcherTest {
+ @Mock
+ private IWindowSession mWindowSession;
+ @Mock
+ private IWindow mWindow;
+ private WindowOnBackInvokedDispatcher mDispatcher;
+ @Mock
+ private OnBackInvokedCallback mCallback1;
+ @Mock
+ private OnBackInvokedCallback mCallback2;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mDispatcher = new WindowOnBackInvokedDispatcher();
+ mDispatcher.attachToWindow(mWindowSession, mWindow);
+ }
+
+ private void waitForIdle() {
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ }
+
+ @Test
+ public void propagatesTopCallback_samePriority() throws RemoteException {
+ ArgumentCaptor<IOnBackInvokedCallback> captor =
+ ArgumentCaptor.forClass(IOnBackInvokedCallback.class);
+
+ mDispatcher.registerOnBackInvokedCallback(
+ mCallback1, OnBackInvokedDispatcher.PRIORITY_DEFAULT);
+ mDispatcher.registerOnBackInvokedCallback(
+ mCallback2, OnBackInvokedDispatcher.PRIORITY_DEFAULT);
+
+ verify(mWindowSession, times(2))
+ .setOnBackInvokedCallback(Mockito.eq(mWindow), captor.capture());
+ captor.getAllValues().get(0).onBackStarted();
+ waitForIdle();
+ verify(mCallback1).onBackStarted();
+ verifyZeroInteractions(mCallback2);
+
+ captor.getAllValues().get(1).onBackStarted();
+ waitForIdle();
+ verify(mCallback2).onBackStarted();
+ verifyNoMoreInteractions(mCallback1);
+ }
+
+ @Test
+ public void propagatesTopCallback_differentPriority() throws RemoteException {
+ ArgumentCaptor<IOnBackInvokedCallback> captor =
+ ArgumentCaptor.forClass(IOnBackInvokedCallback.class);
+
+ mDispatcher.registerOnBackInvokedCallback(
+ mCallback1, OnBackInvokedDispatcher.PRIORITY_OVERLAY);
+ mDispatcher.registerOnBackInvokedCallback(
+ mCallback2, OnBackInvokedDispatcher.PRIORITY_DEFAULT);
+
+ verify(mWindowSession)
+ .setOnBackInvokedCallback(Mockito.eq(mWindow), captor.capture());
+ verifyNoMoreInteractions(mWindowSession);
+ captor.getValue().onBackStarted();
+ waitForIdle();
+ verify(mCallback1).onBackStarted();
+ }
+
+ @Test
+ public void propagatesTopCallback_withRemoval() throws RemoteException {
+ mDispatcher.registerOnBackInvokedCallback(
+ mCallback1, OnBackInvokedDispatcher.PRIORITY_DEFAULT);
+ mDispatcher.registerOnBackInvokedCallback(
+ mCallback2, OnBackInvokedDispatcher.PRIORITY_DEFAULT);
+
+ reset(mWindowSession);
+ mDispatcher.unregisterOnBackInvokedCallback(mCallback1);
+ verifyZeroInteractions(mWindowSession);
+
+ mDispatcher.unregisterOnBackInvokedCallback(mCallback2);
+ verify(mWindowSession).setOnBackInvokedCallback(Mockito.eq(mWindow), isNull());
+ }
+
+
+ @Test
+ public void propagatesTopCallback_sameInstanceAddedTwice() throws RemoteException {
+ ArgumentCaptor<IOnBackInvokedCallback> captor =
+ ArgumentCaptor.forClass(IOnBackInvokedCallback.class);
+
+ mDispatcher.registerOnBackInvokedCallback(mCallback1,
+ OnBackInvokedDispatcher.PRIORITY_OVERLAY);
+ mDispatcher.registerOnBackInvokedCallback(
+ mCallback2, OnBackInvokedDispatcher.PRIORITY_DEFAULT);
+ mDispatcher.registerOnBackInvokedCallback(
+ mCallback1, OnBackInvokedDispatcher.PRIORITY_DEFAULT);
+
+ reset(mWindowSession);
+ mDispatcher.registerOnBackInvokedCallback(
+ mCallback2, OnBackInvokedDispatcher.PRIORITY_OVERLAY);
+ verify(mWindowSession)
+ .setOnBackInvokedCallback(Mockito.eq(mWindow), captor.capture());
+ captor.getValue().onBackStarted();
+ waitForIdle();
+ verify(mCallback2).onBackStarted();
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java b/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java
index 43590bae6770..1f6b57e5d615 100644
--- a/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java
@@ -297,7 +297,7 @@ public class IntentForwarderActivityTest {
mActivityRule.launchActivity(intent);
verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
- verify(sInjector).showToast(anyInt(), anyInt());
+ verify(sInjector).showToast(anyString(), anyInt());
}
@Test
@@ -312,7 +312,7 @@ public class IntentForwarderActivityTest {
mActivityRule.launchActivity(intent);
verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
- verify(sInjector, never()).showToast(anyInt(), anyInt());
+ verify(sInjector, never()).showToast(anyString(), anyInt());
}
@Test
@@ -324,7 +324,7 @@ public class IntentForwarderActivityTest {
mActivityRule.launchActivity(intent);
verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
- verify(sInjector, never()).showToast(anyInt(), anyInt());
+ verify(sInjector, never()).showToast(anyString(), anyInt());
}
@Test
@@ -336,7 +336,7 @@ public class IntentForwarderActivityTest {
mActivityRule.launchActivity(intent);
verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
- verify(sInjector, never()).showToast(anyInt(), anyInt());
+ verify(sInjector, never()).showToast(anyString(), anyInt());
}
@Test
@@ -348,7 +348,7 @@ public class IntentForwarderActivityTest {
mActivityRule.launchActivity(intent);
verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
- verify(sInjector, never()).showToast(anyInt(), anyInt());
+ verify(sInjector, never()).showToast(anyString(), anyInt());
}
@Test
@@ -360,7 +360,7 @@ public class IntentForwarderActivityTest {
mActivityRule.launchActivity(intent);
verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
- verify(sInjector, never()).showToast(anyInt(), anyInt());
+ verify(sInjector, never()).showToast(anyString(), anyInt());
}
@Test
@@ -372,7 +372,7 @@ public class IntentForwarderActivityTest {
mActivityRule.launchActivity(intent);
verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
- verify(sInjector).showToast(anyInt(), anyInt());
+ verify(sInjector).showToast(anyString(), anyInt());
}
@Test
@@ -386,7 +386,7 @@ public class IntentForwarderActivityTest {
mActivityRule.launchActivity(intent);
verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
- verify(sInjector, never()).showToast(anyInt(), anyInt());
+ verify(sInjector, never()).showToast(anyString(), anyInt());
}
@Test
@@ -399,7 +399,7 @@ public class IntentForwarderActivityTest {
mActivityRule.launchActivity(intent);
verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
- verify(sInjector, never()).showToast(anyInt(), anyInt());
+ verify(sInjector, never()).showToast(anyString(), anyInt());
}
@Test
@@ -412,7 +412,7 @@ public class IntentForwarderActivityTest {
mActivityRule.launchActivity(intent);
verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
- verify(sInjector, never()).showToast(anyInt(), anyInt());
+ verify(sInjector, never()).showToast(anyString(), anyInt());
}
@Test
@@ -425,7 +425,7 @@ public class IntentForwarderActivityTest {
mActivityRule.launchActivity(intent);
verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
- verify(sInjector, never()).showToast(anyInt(), anyInt());
+ verify(sInjector, never()).showToast(anyString(), anyInt());
}
@Test
@@ -438,7 +438,7 @@ public class IntentForwarderActivityTest {
mActivityRule.launchActivity(intent);
verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
- verify(sInjector, never()).showToast(anyInt(), anyInt());
+ verify(sInjector, never()).showToast(anyString(), anyInt());
}
@Test
@@ -452,7 +452,7 @@ public class IntentForwarderActivityTest {
mActivityRule.launchActivity(intent);
verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
- verify(sInjector, never()).showToast(anyInt(), anyInt());
+ verify(sInjector, never()).showToast(anyString(), anyInt());
}
@Test
@@ -466,7 +466,7 @@ public class IntentForwarderActivityTest {
mActivityRule.launchActivity(intent);
verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
- verify(sInjector, never()).showToast(anyInt(), anyInt());
+ verify(sInjector, never()).showToast(anyString(), anyInt());
}
@Test
@@ -480,7 +480,7 @@ public class IntentForwarderActivityTest {
mActivityRule.launchActivity(intent);
verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
- verify(sInjector, never()).showToast(anyInt(), anyInt());
+ verify(sInjector, never()).showToast(anyString(), anyInt());
}
@Test
@@ -494,7 +494,7 @@ public class IntentForwarderActivityTest {
mActivityRule.launchActivity(intent);
verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
- verify(sInjector, never()).showToast(anyInt(), anyInt());
+ verify(sInjector, never()).showToast(anyString(), anyInt());
}
@Test
@@ -507,7 +507,7 @@ public class IntentForwarderActivityTest {
mActivityRule.launchActivity(intent);
verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
- verify(sInjector).showToast(anyInt(), anyInt());
+ verify(sInjector).showToast(anyString(), anyInt());
}
@Test
@@ -521,7 +521,7 @@ public class IntentForwarderActivityTest {
mActivityRule.launchActivity(intent);
verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
- verify(sInjector).showToast(anyInt(), anyInt());
+ verify(sInjector).showToast(anyString(), anyInt());
}
@Test
@@ -535,7 +535,7 @@ public class IntentForwarderActivityTest {
mActivityRule.launchActivity(intent);
verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
- verify(sInjector).showToast(anyInt(), anyInt());
+ verify(sInjector).showToast(anyString(), anyInt());
}
@Test
@@ -551,7 +551,7 @@ public class IntentForwarderActivityTest {
mActivityRule.launchActivity(intent);
verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
- verify(sInjector, never()).showToast(anyInt(), anyInt());
+ verify(sInjector, never()).showToast(anyString(), anyInt());
}
@Test
@@ -692,6 +692,6 @@ public class IntentForwarderActivityTest {
}
@Override
- public void showToast(int messageId, int duration) {}
+ public void showToast(String message, int duration) {}
}
}
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java
index 388cf6e15e0b..be8045ddc7b2 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java
@@ -37,8 +37,12 @@ import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.when;
import android.app.ActivityManager;
+import android.bluetooth.BluetoothActivityEnergyInfo;
+import android.bluetooth.UidTraffic;
import android.os.BatteryStats;
+import android.os.BluetoothBatteryStats;
import android.os.WakeLockStats;
+import android.os.WorkSource;
import android.util.SparseArray;
import android.view.Display;
@@ -47,6 +51,8 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader;
+import com.google.common.collect.ImmutableList;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -66,6 +72,8 @@ public class BatteryStatsImplTest {
private KernelCpuUidFreqTimeReader mKernelUidCpuFreqTimeReader;
@Mock
private KernelSingleUidTimeReader mKernelSingleUidTimeReader;
+ @Mock
+ private PowerProfile mPowerProfile;
private final MockClock mMockClock = new MockClock();
private MockBatteryStatsImpl mBatteryStatsImpl;
@@ -79,6 +87,7 @@ public class BatteryStatsImplTest {
when(mKernelUidCpuFreqTimeReader.allUidTimesAvailable()).thenReturn(true);
when(mKernelSingleUidTimeReader.singleUidCpuTimesAvailable()).thenReturn(true);
mBatteryStatsImpl = new MockBatteryStatsImpl(mMockClock)
+ .setPowerProfile(mPowerProfile)
.setKernelCpuUidFreqTimeReader(mKernelUidCpuFreqTimeReader)
.setKernelSingleUidTimeReader(mKernelSingleUidTimeReader);
}
@@ -559,4 +568,38 @@ public class BatteryStatsImplTest {
assertThat(wakeLock2.timeHeldMs).isEqualTo(3000); // 9000-6000
assertThat(wakeLock2.totalTimeHeldMs).isEqualTo(4000); // (5000-4000) + (9000-6000)
}
+
+ @Test
+ public void testGetBluetoothBatteryStats() {
+ when(mPowerProfile.getAveragePower(
+ PowerProfile.POWER_BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE)).thenReturn(3.0);
+ mBatteryStatsImpl.setOnBatteryInternal(true);
+ mBatteryStatsImpl.updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0);
+
+ final WorkSource ws = new WorkSource(10042);
+ mBatteryStatsImpl.noteBluetoothScanStartedFromSourceLocked(ws, false, 1000, 1000);
+ mBatteryStatsImpl.noteBluetoothScanStoppedFromSourceLocked(ws, false, 5000, 5000);
+ mBatteryStatsImpl.noteBluetoothScanStartedFromSourceLocked(ws, true, 6000, 6000);
+ mBatteryStatsImpl.noteBluetoothScanStoppedFromSourceLocked(ws, true, 9000, 9000);
+ mBatteryStatsImpl.noteBluetoothScanResultsFromSourceLocked(ws, 42, 9000, 9000);
+
+ BluetoothActivityEnergyInfo info = new BluetoothActivityEnergyInfo(1000,
+ BluetoothActivityEnergyInfo.BT_STACK_STATE_STATE_ACTIVE, 9000, 8000, 12000, 0);
+ info.setUidTraffic(ImmutableList.of(
+ new UidTraffic(10042, 3000, 4000),
+ new UidTraffic(10043, 5000, 8000)));
+ mBatteryStatsImpl.updateBluetoothStateLocked(info, -1, 1000, 1000);
+
+ BluetoothBatteryStats stats =
+ mBatteryStatsImpl.getBluetoothBatteryStats();
+ assertThat(stats.getUidStats()).hasSize(2);
+
+ final BluetoothBatteryStats.UidStats uidStats =
+ stats.getUidStats().stream().filter(u -> u.uid == 10042).findFirst().get();
+ assertThat(uidStats.scanTimeMs).isEqualTo(7000); // 4000+3000
+ assertThat(uidStats.unoptimizedScanTimeMs).isEqualTo(3000);
+ assertThat(uidStats.scanResultCount).isEqualTo(42);
+ assertThat(uidStats.rxTimeMs).isEqualTo(7375); // Some scan time is treated as RX
+ assertThat(uidStats.txTimeMs).isEqualTo(7666); // Some scan time is treated as TX
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
index 9699275d3037..8cc4c348111c 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
@@ -83,7 +83,7 @@ public class BatteryUsageStatsTest {
final Parcel parcel = Parcel.obtain();
parcel.writeParcelable(outBatteryUsageStats, 0);
- assertThat(parcel.dataSize()).isLessThan(6000);
+ assertThat(parcel.dataSize()).isLessThan(7000);
parcel.setDataPosition(0);
diff --git a/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java
index d361da95a1b9..ed035e5166b3 100644
--- a/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java
@@ -25,18 +25,21 @@ import android.os.BatteryConsumer;
import android.os.BatteryStats;
import android.os.BatteryUsageStatsQuery;
import android.os.Process;
+import android.os.UidBatteryConsumer;
+import android.os.WorkSource;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.google.common.collect.ImmutableList;
+
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.util.ArrayList;
-
@RunWith(AndroidJUnit4.class)
@SmallTest
+@SuppressWarnings("GuardedBy")
public class BluetoothPowerCalculatorTest {
private static final double PRECISION = 0.00001;
private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42;
@@ -50,6 +53,12 @@ public class BluetoothPowerCalculatorTest {
@Test
public void testTimerBasedModel() {
+ BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
+
+ final WorkSource ws = new WorkSource(APP_UID);
+ batteryStats.noteBluetoothScanStartedFromSourceLocked(ws, false, 0, 0);
+ batteryStats.noteBluetoothScanStoppedFromSourceLocked(ws, false, 1000, 1000);
+
setupBluetoothEnergyInfo(0, BatteryStats.POWER_DATA_UNAVAILABLE);
BluetoothPowerCalculator calculator =
@@ -57,8 +66,81 @@ public class BluetoothPowerCalculatorTest {
mStatsRule.apply(BatteryUsageStatsRule.POWER_PROFILE_MODEL_ONLY, calculator);
- assertCalculatedPower(0.08216, 0.18169, 0.26388, 0.26386,
- BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+ assertBluetoothPowerAndDuration(
+ mStatsRule.getUidBatteryConsumer(Process.BLUETOOTH_UID),
+ 0.06944, 3000, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+ assertBluetoothPowerAndDuration(
+ mStatsRule.getUidBatteryConsumer(APP_UID),
+ 0.19444, 9000, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+ assertBluetoothPowerAndDuration(
+ mStatsRule.getDeviceBatteryConsumer(),
+ 0.26388, 12000, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+ assertBluetoothPowerAndDuration(
+ mStatsRule.getAppsBatteryConsumer(),
+ 0.26388, 12000, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+ }
+
+ @Test
+ public void testTimerBasedModel_byProcessState() {
+ mStatsRule.setTime(1000, 1000);
+
+ BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
+
+ BatteryStatsImpl.Uid uid = batteryStats.getUidStatsLocked(APP_UID);
+ uid.setProcessStateForTest(
+ BatteryStats.Uid.PROCESS_STATE_FOREGROUND, 1000);
+
+ BluetoothActivityEnergyInfo info1 = new BluetoothActivityEnergyInfo(2000,
+ BluetoothActivityEnergyInfo.BT_STACK_STATE_STATE_ACTIVE, 1000, 2000, 3000, 4000);
+ info1.setUidTraffic(ImmutableList.of(
+ new UidTraffic(Process.BLUETOOTH_UID, 1000, 2000),
+ new UidTraffic(APP_UID, 3000, 4000)));
+
+ batteryStats.updateBluetoothStateLocked(info1,
+ 0/*1_000_000*/, 2000, 2000);
+
+ uid.setProcessStateForTest(
+ BatteryStats.Uid.PROCESS_STATE_BACKGROUND, 3000);
+
+ BluetoothActivityEnergyInfo info2 = new BluetoothActivityEnergyInfo(4000,
+ BluetoothActivityEnergyInfo.BT_STACK_STATE_STATE_ACTIVE, 5000, 6000, 7000, 8000);
+ info2.setUidTraffic(ImmutableList.of(
+ new UidTraffic(Process.BLUETOOTH_UID, 5000, 6000),
+ new UidTraffic(APP_UID, 7000, 8000)));
+
+ batteryStats.updateBluetoothStateLocked(info2,
+ 0 /*5_000_000 */, 4000, 4000);
+
+ BluetoothPowerCalculator calculator =
+ new BluetoothPowerCalculator(mStatsRule.getPowerProfile());
+
+ mStatsRule.apply(new BatteryUsageStatsQuery.Builder()
+ .powerProfileModeledOnly()
+ .includePowerModels()
+ .includeProcessStateData()
+ .build(), calculator);
+
+ UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID);
+ assertThat(uidConsumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_BLUETOOTH))
+ .isEqualTo(6166);
+ assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_BLUETOOTH))
+ .isWithin(PRECISION).of(0.1226666);
+ assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_BLUETOOTH))
+ .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+
+ final BatteryConsumer.Key foreground = uidConsumer.getKey(
+ BatteryConsumer.POWER_COMPONENT_BLUETOOTH,
+ BatteryConsumer.PROCESS_STATE_FOREGROUND);
+ final BatteryConsumer.Key background = uidConsumer.getKey(
+ BatteryConsumer.POWER_COMPONENT_BLUETOOTH,
+ BatteryConsumer.PROCESS_STATE_BACKGROUND);
+ final BatteryConsumer.Key fgs = uidConsumer.getKey(
+ BatteryConsumer.POWER_COMPONENT_BLUETOOTH,
+ BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE);
+
+ assertThat(uidConsumer.getConsumedPower(foreground)).isWithin(PRECISION).of(0.081);
+ assertThat(uidConsumer.getConsumedPower(background)).isWithin(PRECISION).of(0.0416666);
+ assertThat(uidConsumer.getConsumedPower(fgs)).isWithin(PRECISION).of(0);
}
@Test
@@ -71,8 +153,18 @@ public class BluetoothPowerCalculatorTest {
mStatsRule.apply(new BatteryUsageStatsQuery.Builder().includePowerModels().build(),
calculator);
- assertCalculatedPower(0.08216, 0.18169, 0.30030, 0.26386,
- BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+ assertBluetoothPowerAndDuration(
+ mStatsRule.getUidBatteryConsumer(Process.BLUETOOTH_UID),
+ 0.08216, 3583, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+ assertBluetoothPowerAndDuration(
+ mStatsRule.getUidBatteryConsumer(APP_UID),
+ 0.18169, 8416, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+ assertBluetoothPowerAndDuration(
+ mStatsRule.getDeviceBatteryConsumer(),
+ 0.30030, 12000, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+ assertBluetoothPowerAndDuration(
+ mStatsRule.getAppsBatteryConsumer(),
+ 0.26386, 11999, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
}
@Test
@@ -85,10 +177,84 @@ public class BluetoothPowerCalculatorTest {
mStatsRule.apply(calculator);
- assertCalculatedPower(0.10378, 0.22950, 0.33333, 0.33329,
- BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
+ assertBluetoothPowerAndDuration(
+ mStatsRule.getUidBatteryConsumer(Process.BLUETOOTH_UID),
+ 0.10378, 3583, BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
+ assertBluetoothPowerAndDuration(
+ mStatsRule.getUidBatteryConsumer(APP_UID),
+ 0.22950, 8416, BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
+ assertBluetoothPowerAndDuration(
+ mStatsRule.getDeviceBatteryConsumer(),
+ 0.33333, 12000, BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
+ assertBluetoothPowerAndDuration(
+ mStatsRule.getAppsBatteryConsumer(),
+ 0.33329, 11999, BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
+ }
+
+ @Test
+ public void testMeasuredEnergyBasedModel_byProcessState() {
+ mStatsRule.initMeasuredEnergyStatsLocked();
+ mStatsRule.setTime(1000, 1000);
+
+ BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
+
+ BatteryStatsImpl.Uid uid = batteryStats.getUidStatsLocked(APP_UID);
+ uid.setProcessStateForTest(
+ BatteryStats.Uid.PROCESS_STATE_FOREGROUND, 1000);
+
+ BluetoothActivityEnergyInfo info1 = new BluetoothActivityEnergyInfo(2000,
+ BluetoothActivityEnergyInfo.BT_STACK_STATE_STATE_ACTIVE, 1000, 2000, 3000, 4000);
+ info1.setUidTraffic(ImmutableList.of(
+ new UidTraffic(Process.BLUETOOTH_UID, 1000, 2000),
+ new UidTraffic(APP_UID, 3000, 4000)));
+
+ batteryStats.updateBluetoothStateLocked(info1,
+ 1_000_000, 2000, 2000);
+
+ uid.setProcessStateForTest(
+ BatteryStats.Uid.PROCESS_STATE_BACKGROUND, 3000);
+
+ BluetoothActivityEnergyInfo info2 = new BluetoothActivityEnergyInfo(4000,
+ BluetoothActivityEnergyInfo.BT_STACK_STATE_STATE_ACTIVE, 5000, 6000, 7000, 8000);
+ info2.setUidTraffic(ImmutableList.of(
+ new UidTraffic(Process.BLUETOOTH_UID, 5000, 6000),
+ new UidTraffic(APP_UID, 7000, 8000)));
+
+ batteryStats.updateBluetoothStateLocked(info2,
+ 5_000_000, 4000, 4000);
+
+ BluetoothPowerCalculator calculator =
+ new BluetoothPowerCalculator(mStatsRule.getPowerProfile());
+
+ mStatsRule.apply(new BatteryUsageStatsQuery.Builder()
+ .includePowerModels()
+ .includeProcessStateData()
+ .build(), calculator);
+
+ UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID);
+ assertThat(uidConsumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_BLUETOOTH))
+ .isEqualTo(6166);
+ assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_BLUETOOTH))
+ .isWithin(PRECISION).of(0.8220561);
+ assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_BLUETOOTH))
+ .isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
+
+ final BatteryConsumer.Key foreground = uidConsumer.getKey(
+ BatteryConsumer.POWER_COMPONENT_BLUETOOTH,
+ BatteryConsumer.PROCESS_STATE_FOREGROUND);
+ final BatteryConsumer.Key background = uidConsumer.getKey(
+ BatteryConsumer.POWER_COMPONENT_BLUETOOTH,
+ BatteryConsumer.PROCESS_STATE_BACKGROUND);
+ final BatteryConsumer.Key fgs = uidConsumer.getKey(
+ BatteryConsumer.POWER_COMPONENT_BLUETOOTH,
+ BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE);
+
+ assertThat(uidConsumer.getConsumedPower(foreground)).isWithin(PRECISION).of(0.4965352);
+ assertThat(uidConsumer.getConsumedPower(background)).isWithin(PRECISION).of(0.3255208);
+ assertThat(uidConsumer.getConsumedPower(fgs)).isWithin(PRECISION).of(0);
}
+
@Test
public void testIgnoreMeasuredEnergyBasedModel() {
mStatsRule.initMeasuredEnergyStatsLocked();
@@ -99,36 +265,29 @@ public class BluetoothPowerCalculatorTest {
mStatsRule.apply(BatteryUsageStatsRule.POWER_PROFILE_MODEL_ONLY, calculator);
- assertCalculatedPower(0.08216, 0.18169, 0.26388, 0.26386,
- BatteryConsumer.POWER_MODEL_POWER_PROFILE);
- }
-
- private void setupBluetoothEnergyInfo(long reportedEnergyUc, long consumedEnergyUc) {
- final BluetoothActivityEnergyInfo info = new BluetoothActivityEnergyInfo(1000,
- BluetoothActivityEnergyInfo.BT_STACK_STATE_STATE_ACTIVE, 7000, 5000, 0,
- reportedEnergyUc);
- info.setUidTraffic(new ArrayList<UidTraffic>(){{
- add(new UidTraffic(Process.BLUETOOTH_UID, 1000, 2000));
- add(new UidTraffic(APP_UID, 3000, 4000));
- }});
- mStatsRule.getBatteryStats().updateBluetoothStateLocked(info,
- consumedEnergyUc, 1000, 1000);
- }
-
- private void assertCalculatedPower(double bluetoothUidPowerMah, double appPowerMah,
- double devicePowerMah, double allAppsPowerMah, int powerModelPowerProfile) {
assertBluetoothPowerAndDuration(
mStatsRule.getUidBatteryConsumer(Process.BLUETOOTH_UID),
- bluetoothUidPowerMah, 3583, powerModelPowerProfile);
+ 0.08216, 3583, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
assertBluetoothPowerAndDuration(
mStatsRule.getUidBatteryConsumer(APP_UID),
- appPowerMah, 8416, powerModelPowerProfile);
+ 0.18169, 8416, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
assertBluetoothPowerAndDuration(
mStatsRule.getDeviceBatteryConsumer(),
- devicePowerMah, 12000, powerModelPowerProfile);
+ 0.26388, 12000, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
assertBluetoothPowerAndDuration(
mStatsRule.getAppsBatteryConsumer(),
- allAppsPowerMah, 11999, powerModelPowerProfile);
+ 0.26386, 11999, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+ }
+
+ private void setupBluetoothEnergyInfo(long reportedEnergyUc, long consumedEnergyUc) {
+ final BluetoothActivityEnergyInfo info = new BluetoothActivityEnergyInfo(1000,
+ BluetoothActivityEnergyInfo.BT_STACK_STATE_STATE_ACTIVE, 7000, 5000, 0,
+ reportedEnergyUc);
+ info.setUidTraffic(ImmutableList.of(
+ new UidTraffic(Process.BLUETOOTH_UID, 1000, 2000),
+ new UidTraffic(APP_UID, 3000, 4000)));
+ mStatsRule.getBatteryStats().updateBluetoothStateLocked(info,
+ consumedEnergyUc, 1000, 1000);
}
private void assertBluetoothPowerAndDuration(@Nullable BatteryConsumer batteryConsumer,
diff --git a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
index bddb3a1906fd..1bb41a8cfffd 100644
--- a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
+++ b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
@@ -43,6 +43,7 @@ import java.util.concurrent.Future;
*/
public class MockBatteryStatsImpl extends BatteryStatsImpl {
public boolean mForceOnBattery;
+ // The mNetworkStats will be used for both wifi and mobile categories
private NetworkStats mNetworkStats;
private DummyExternalStatsSync mExternalStatsSync = new DummyExternalStatsSync();
@@ -118,11 +119,16 @@ public class MockBatteryStatsImpl extends BatteryStatsImpl {
}
@Override
- protected NetworkStats readNetworkStatsLocked(@NonNull NetworkStatsManager networkStatsManager,
- String[] ifaces) {
+ protected NetworkStats readMobileNetworkStatsLocked(
+ @NonNull NetworkStatsManager networkStatsManager) {
return mNetworkStats;
}
+ @Override
+ protected NetworkStats readWifiNetworkStatsLocked(
+ @NonNull NetworkStatsManager networkStatsManager) {
+ return mNetworkStats;
+ }
public MockBatteryStatsImpl setPowerProfile(PowerProfile powerProfile) {
mPowerProfile = powerProfile;
return this;
diff --git a/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java b/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java
index 88ee405483db..bc3b4229f5e5 100644
--- a/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java
@@ -21,29 +21,49 @@ import static com.android.internal.os.PowerProfile.POWER_GROUP_DISPLAY_AMBIENT;
import static com.android.internal.os.PowerProfile.POWER_GROUP_DISPLAY_SCREEN_FULL;
import static com.android.internal.os.PowerProfile.POWER_GROUP_DISPLAY_SCREEN_ON;
+import android.annotation.XmlRes;
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.XmlResourceParser;
+
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
+import com.android.frameworks.coretests.R;
+import com.android.internal.power.ModemPowerProfile;
+import com.android.internal.util.XmlUtils;
+
import junit.framework.TestCase;
import org.junit.Before;
import org.junit.Test;
/*
- * Keep this file in sync with frameworks/base/core/res/res/xml/power_profile_test.xml
+ * Keep this file in sync with frameworks/base/core/res/res/xml/power_profile_test.xml and
+ * frameworks/base/core/tests/coretests/res/xml/power_profile_test_modem.xml
+ *
+ * Run with:
+ * atest com.android.internal.os.PowerProfileTest
*/
@SmallTest
public class PowerProfileTest extends TestCase {
+ static final String TAG_TEST_MODEM = "test-modem";
+ static final String ATTR_NAME = "name";
+
private PowerProfile mProfile;
+ private Context mContext;
@Before
public void setUp() {
- mProfile = new PowerProfile(InstrumentationRegistry.getContext(), true);
+ mContext = InstrumentationRegistry.getContext();
+ mProfile = new PowerProfile(mContext);
}
@Test
public void testPowerProfile() {
+ mProfile.forceInitForTesting(mContext, R.xml.power_profile_test);
+
assertEquals(2, mProfile.getNumCpuClusters());
assertEquals(4, mProfile.getNumCoresInCpuCluster(0));
assertEquals(4, mProfile.getNumCoresInCpuCluster(1));
@@ -65,6 +85,435 @@ public class PowerProfileTest extends TestCase {
mProfile.getAveragePowerForOrdinal(POWER_GROUP_DISPLAY_SCREEN_FULL, 0));
assertEquals(100.0, mProfile.getAveragePower(PowerProfile.POWER_AUDIO));
assertEquals(150.0, mProfile.getAveragePower(PowerProfile.POWER_VIDEO));
+
+ assertEquals(123.0, mProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_SLEEP));
+ assertEquals(456.0, mProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_IDLE));
+ assertEquals(789.0, mProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_RX));
+ assertEquals(10.0, mProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_TX, 0));
+ assertEquals(20.0, mProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_TX, 1));
+ assertEquals(30.0, mProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_TX, 2));
+ assertEquals(40.0, mProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_TX, 3));
+ assertEquals(50.0, mProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_TX, 4));
+
+ // Deprecated Modem constants should work with current format.
+ assertEquals(123.0, mProfile.getAverageBatteryDrainMa(
+ PowerProfile.SUBSYSTEM_MODEM | ModemPowerProfile.MODEM_DRAIN_TYPE_SLEEP));
+ assertEquals(456.0, mProfile.getAverageBatteryDrainMa(
+ PowerProfile.SUBSYSTEM_MODEM | ModemPowerProfile.MODEM_DRAIN_TYPE_IDLE));
+ assertEquals(789.0, mProfile.getAverageBatteryDrainMa(
+ PowerProfile.SUBSYSTEM_MODEM | ModemPowerProfile.MODEM_DRAIN_TYPE_RX));
+ assertEquals(10.0, mProfile.getAverageBatteryDrainMa(
+ PowerProfile.SUBSYSTEM_MODEM | ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT
+ | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+ | ModemPowerProfile.MODEM_TX_LEVEL_0));
+ assertEquals(20.0, mProfile.getAverageBatteryDrainMa(
+ PowerProfile.SUBSYSTEM_MODEM | ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT
+ | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+ | ModemPowerProfile.MODEM_TX_LEVEL_1));
+ assertEquals(30.0, mProfile.getAverageBatteryDrainMa(
+ PowerProfile.SUBSYSTEM_MODEM | ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT
+ | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+ | ModemPowerProfile.MODEM_TX_LEVEL_2));
+ assertEquals(40.0, mProfile.getAverageBatteryDrainMa(
+ PowerProfile.SUBSYSTEM_MODEM | ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT
+ | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+ | ModemPowerProfile.MODEM_TX_LEVEL_3));
+ assertEquals(50.0, mProfile.getAverageBatteryDrainMa(
+ PowerProfile.SUBSYSTEM_MODEM | ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT
+ | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+ | ModemPowerProfile.MODEM_TX_LEVEL_4));
+ }
+
+ @Test
+ public void testModemPowerProfile_defaultRat() throws Exception {
+ final XmlResourceParser parser = getTestModemElement(R.xml.power_profile_test_modem,
+ "testModemPowerProfile_defaultRat");
+ ModemPowerProfile mpp = new ModemPowerProfile();
+ mpp.parseFromXml(parser);
+ assertEquals(10.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_DRAIN_TYPE_SLEEP));
+ assertEquals(20.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_DRAIN_TYPE_IDLE));
+
+ // Only default RAT was defined, all other RAT's should fallback to the default value.
+ assertEquals(30.0, mpp.getAverageBatteryDrainMa(
+ ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_RX));
+ assertEquals(30.0, mpp.getAverageBatteryDrainMa(
+ ModemPowerProfile.MODEM_RAT_TYPE_LTE | ModemPowerProfile.MODEM_DRAIN_TYPE_RX));
+ assertEquals(30.0, mpp.getAverageBatteryDrainMa(
+ ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_DRAIN_TYPE_RX));
+
+ assertEquals(40.0, mpp.getAverageBatteryDrainMa(
+ ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+ | ModemPowerProfile.MODEM_TX_LEVEL_0));
+ assertEquals(40.0, mpp.getAverageBatteryDrainMa(
+ ModemPowerProfile.MODEM_RAT_TYPE_LTE | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+ | ModemPowerProfile.MODEM_TX_LEVEL_0));
+ assertEquals(40.0, mpp.getAverageBatteryDrainMa(
+ ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+ | ModemPowerProfile.MODEM_TX_LEVEL_0));
+
+ assertEquals(50.0, mpp.getAverageBatteryDrainMa(
+ ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+ | ModemPowerProfile.MODEM_TX_LEVEL_1));
+ assertEquals(50.0, mpp.getAverageBatteryDrainMa(
+ ModemPowerProfile.MODEM_RAT_TYPE_LTE | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+ | ModemPowerProfile.MODEM_TX_LEVEL_1));
+ assertEquals(50.0, mpp.getAverageBatteryDrainMa(
+ ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+ | ModemPowerProfile.MODEM_TX_LEVEL_1));
+
+ assertEquals(60.0, mpp.getAverageBatteryDrainMa(
+ ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+ | ModemPowerProfile.MODEM_TX_LEVEL_2));
+ assertEquals(60.0, mpp.getAverageBatteryDrainMa(
+ ModemPowerProfile.MODEM_RAT_TYPE_LTE | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+ | ModemPowerProfile.MODEM_TX_LEVEL_2));
+ assertEquals(60.0, mpp.getAverageBatteryDrainMa(
+ ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+ | ModemPowerProfile.MODEM_TX_LEVEL_2));
+
+ assertEquals(70.0, mpp.getAverageBatteryDrainMa(
+ ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+ | ModemPowerProfile.MODEM_TX_LEVEL_3));
+ assertEquals(70.0, mpp.getAverageBatteryDrainMa(
+ ModemPowerProfile.MODEM_RAT_TYPE_LTE | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+ | ModemPowerProfile.MODEM_TX_LEVEL_3));
+ assertEquals(70.0, mpp.getAverageBatteryDrainMa(
+ ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+ | ModemPowerProfile.MODEM_TX_LEVEL_3));
+
+ assertEquals(80.0, mpp.getAverageBatteryDrainMa(
+ ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+ | ModemPowerProfile.MODEM_TX_LEVEL_4));
+ assertEquals(80.0, mpp.getAverageBatteryDrainMa(
+ ModemPowerProfile.MODEM_RAT_TYPE_LTE | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+ | ModemPowerProfile.MODEM_TX_LEVEL_4));
+ assertEquals(80.0, mpp.getAverageBatteryDrainMa(
+ ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+ | ModemPowerProfile.MODEM_TX_LEVEL_4));
}
+ @Test
+ public void testModemPowerProfile_partiallyDefined() throws Exception {
+ final XmlResourceParser parser = getTestModemElement(R.xml.power_profile_test_modem,
+ "testModemPowerProfile_partiallyDefined");
+ ModemPowerProfile mpp = new ModemPowerProfile();
+ mpp.parseFromXml(parser);
+ assertEquals(1.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_DRAIN_TYPE_SLEEP));
+ assertEquals(2.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_DRAIN_TYPE_IDLE));
+
+ assertEquals(3.0, mpp.getAverageBatteryDrainMa(
+ ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_RX));
+ assertEquals(4.0, mpp.getAverageBatteryDrainMa(
+ ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+ | ModemPowerProfile.MODEM_TX_LEVEL_0));
+ assertEquals(5.0, mpp.getAverageBatteryDrainMa(
+ ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+ | ModemPowerProfile.MODEM_TX_LEVEL_1));
+ assertEquals(6.0, mpp.getAverageBatteryDrainMa(
+ ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+ | ModemPowerProfile.MODEM_TX_LEVEL_2));
+ assertEquals(7.0, mpp.getAverageBatteryDrainMa(
+ ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+ | ModemPowerProfile.MODEM_TX_LEVEL_3));
+ assertEquals(8.0, mpp.getAverageBatteryDrainMa(
+ ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+ | ModemPowerProfile.MODEM_TX_LEVEL_4));
+
+ // LTE RAT power constants were not defined, fallback to defaults
+ assertEquals(3.0, mpp.getAverageBatteryDrainMa(
+ ModemPowerProfile.MODEM_RAT_TYPE_LTE | ModemPowerProfile.MODEM_DRAIN_TYPE_RX));
+ assertEquals(4.0, mpp.getAverageBatteryDrainMa(
+ ModemPowerProfile.MODEM_RAT_TYPE_LTE | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+ | ModemPowerProfile.MODEM_TX_LEVEL_0));
+ assertEquals(5.0, mpp.getAverageBatteryDrainMa(
+ ModemPowerProfile.MODEM_RAT_TYPE_LTE | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+ | ModemPowerProfile.MODEM_TX_LEVEL_1));
+ assertEquals(6.0, mpp.getAverageBatteryDrainMa(
+ ModemPowerProfile.MODEM_RAT_TYPE_LTE | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+ | ModemPowerProfile.MODEM_TX_LEVEL_2));
+ assertEquals(7.0, mpp.getAverageBatteryDrainMa(
+ ModemPowerProfile.MODEM_RAT_TYPE_LTE | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+ | ModemPowerProfile.MODEM_TX_LEVEL_3));
+ assertEquals(8.0, mpp.getAverageBatteryDrainMa(
+ ModemPowerProfile.MODEM_RAT_TYPE_LTE | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+ | ModemPowerProfile.MODEM_TX_LEVEL_4));
+
+ assertEquals(13.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+ | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_DEFAULT
+ | ModemPowerProfile.MODEM_DRAIN_TYPE_RX));
+ assertEquals(14.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+ | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_DEFAULT
+ | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_0));
+ assertEquals(15.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+ | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_DEFAULT
+ | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_1));
+ assertEquals(16.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+ | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_DEFAULT
+ | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_2));
+ assertEquals(17.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+ | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_DEFAULT
+ | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_3));
+ assertEquals(18.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+ | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_DEFAULT
+ | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_4));
+
+ // Non-mmwave NR frequency power constants were not defined, fallback to defaults
+ assertEquals(13.0, mpp.getAverageBatteryDrainMa(
+ ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_LOW
+ | ModemPowerProfile.MODEM_DRAIN_TYPE_RX));
+ assertEquals(14.0, mpp.getAverageBatteryDrainMa(
+ ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_LOW
+ | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+ | ModemPowerProfile.MODEM_TX_LEVEL_0));
+ assertEquals(15.0, mpp.getAverageBatteryDrainMa(
+ ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_LOW
+ | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+ | ModemPowerProfile.MODEM_TX_LEVEL_1));
+ assertEquals(16.0, mpp.getAverageBatteryDrainMa(
+ ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_LOW
+ | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+ | ModemPowerProfile.MODEM_TX_LEVEL_2));
+ assertEquals(17.0, mpp.getAverageBatteryDrainMa(
+ ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_LOW
+ | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+ | ModemPowerProfile.MODEM_TX_LEVEL_3));
+ assertEquals(18.0, mpp.getAverageBatteryDrainMa(
+ ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_LOW
+ | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+ | ModemPowerProfile.MODEM_TX_LEVEL_4));
+
+ assertEquals(13.0, mpp.getAverageBatteryDrainMa(
+ ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MID
+ | ModemPowerProfile.MODEM_DRAIN_TYPE_RX));
+ assertEquals(14.0, mpp.getAverageBatteryDrainMa(
+ ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MID
+ | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+ | ModemPowerProfile.MODEM_TX_LEVEL_0));
+ assertEquals(15.0, mpp.getAverageBatteryDrainMa(
+ ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MID
+ | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+ | ModemPowerProfile.MODEM_TX_LEVEL_1));
+ assertEquals(16.0, mpp.getAverageBatteryDrainMa(
+ ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MID
+ | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+ | ModemPowerProfile.MODEM_TX_LEVEL_2));
+ assertEquals(17.0, mpp.getAverageBatteryDrainMa(
+ ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MID
+ | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+ | ModemPowerProfile.MODEM_TX_LEVEL_3));
+ assertEquals(18.0, mpp.getAverageBatteryDrainMa(
+ ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MID
+ | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+ | ModemPowerProfile.MODEM_TX_LEVEL_4));
+
+ assertEquals(13.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+ | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_HIGH
+ | ModemPowerProfile.MODEM_DRAIN_TYPE_RX));
+ assertEquals(14.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+ | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_HIGH
+ | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_0));
+ assertEquals(15.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+ | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_HIGH
+ | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_1));
+ assertEquals(16.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+ | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_HIGH
+ | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_2));
+ assertEquals(17.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+ | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_HIGH
+ | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_3));
+ assertEquals(18.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+ | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_HIGH
+ | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_4));
+
+ assertEquals(53.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+ | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MMWAVE
+ | ModemPowerProfile.MODEM_DRAIN_TYPE_RX));
+ assertEquals(54.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+ | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MMWAVE
+ | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_0));
+ assertEquals(55.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+ | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MMWAVE
+ | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_1));
+ assertEquals(56.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+ | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MMWAVE
+ | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_2));
+ assertEquals(57.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+ | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MMWAVE
+ | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_3));
+ assertEquals(58.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+ | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MMWAVE
+ | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_4));
+ }
+
+ @Test
+ public void testModemPowerProfile_fullyDefined() throws Exception {
+ final XmlResourceParser parser = getTestModemElement(R.xml.power_profile_test_modem,
+ "testModemPowerProfile_fullyDefined");
+ ModemPowerProfile mpp = new ModemPowerProfile();
+ mpp.parseFromXml(parser);
+ assertEquals(1.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_DRAIN_TYPE_SLEEP));
+ assertEquals(2.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_DRAIN_TYPE_IDLE));
+
+ // Only default RAT was defined, all other RAT's should fallback to the default value.
+ assertEquals(3.0, mpp.getAverageBatteryDrainMa(
+ ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_RX));
+ assertEquals(4.0, mpp.getAverageBatteryDrainMa(
+ ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+ | ModemPowerProfile.MODEM_TX_LEVEL_0));
+ assertEquals(5.0, mpp.getAverageBatteryDrainMa(
+ ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+ | ModemPowerProfile.MODEM_TX_LEVEL_1));
+ assertEquals(6.0, mpp.getAverageBatteryDrainMa(
+ ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+ | ModemPowerProfile.MODEM_TX_LEVEL_2));
+ assertEquals(7.0, mpp.getAverageBatteryDrainMa(
+ ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+ | ModemPowerProfile.MODEM_TX_LEVEL_3));
+ assertEquals(8.0, mpp.getAverageBatteryDrainMa(
+ ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+ | ModemPowerProfile.MODEM_TX_LEVEL_4));
+
+ assertEquals(10.0, mpp.getAverageBatteryDrainMa(
+ ModemPowerProfile.MODEM_RAT_TYPE_LTE | ModemPowerProfile.MODEM_DRAIN_TYPE_RX));
+ assertEquals(20.0, mpp.getAverageBatteryDrainMa(
+ ModemPowerProfile.MODEM_RAT_TYPE_LTE | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+ | ModemPowerProfile.MODEM_TX_LEVEL_0));
+ assertEquals(30.0, mpp.getAverageBatteryDrainMa(
+ ModemPowerProfile.MODEM_RAT_TYPE_LTE | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+ | ModemPowerProfile.MODEM_TX_LEVEL_1));
+ assertEquals(40.0, mpp.getAverageBatteryDrainMa(
+ ModemPowerProfile.MODEM_RAT_TYPE_LTE | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+ | ModemPowerProfile.MODEM_TX_LEVEL_2));
+ assertEquals(50.0, mpp.getAverageBatteryDrainMa(
+ ModemPowerProfile.MODEM_RAT_TYPE_LTE | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+ | ModemPowerProfile.MODEM_TX_LEVEL_3));
+ assertEquals(60.0, mpp.getAverageBatteryDrainMa(
+ ModemPowerProfile.MODEM_RAT_TYPE_LTE | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+ | ModemPowerProfile.MODEM_TX_LEVEL_4));
+
+ assertEquals(13.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+ | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_DEFAULT
+ | ModemPowerProfile.MODEM_DRAIN_TYPE_RX));
+ assertEquals(14.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+ | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_DEFAULT
+ | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_0));
+ assertEquals(15.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+ | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_DEFAULT
+ | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_1));
+ assertEquals(16.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+ | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_DEFAULT
+ | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_2));
+ assertEquals(17.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+ | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_DEFAULT
+ | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_3));
+ assertEquals(18.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+ | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_DEFAULT
+ | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_4));
+
+ assertEquals(23.0, mpp.getAverageBatteryDrainMa(
+ ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_LOW
+ | ModemPowerProfile.MODEM_DRAIN_TYPE_RX));
+ assertEquals(24.0, mpp.getAverageBatteryDrainMa(
+ ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_LOW
+ | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+ | ModemPowerProfile.MODEM_TX_LEVEL_0));
+ assertEquals(25.0, mpp.getAverageBatteryDrainMa(
+ ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_LOW
+ | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+ | ModemPowerProfile.MODEM_TX_LEVEL_1));
+ assertEquals(26.0, mpp.getAverageBatteryDrainMa(
+ ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_LOW
+ | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+ | ModemPowerProfile.MODEM_TX_LEVEL_2));
+ assertEquals(27.0, mpp.getAverageBatteryDrainMa(
+ ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_LOW
+ | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+ | ModemPowerProfile.MODEM_TX_LEVEL_3));
+ assertEquals(28.0, mpp.getAverageBatteryDrainMa(
+ ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_LOW
+ | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+ | ModemPowerProfile.MODEM_TX_LEVEL_4));
+
+ assertEquals(33.0, mpp.getAverageBatteryDrainMa(
+ ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MID
+ | ModemPowerProfile.MODEM_DRAIN_TYPE_RX));
+ assertEquals(34.0, mpp.getAverageBatteryDrainMa(
+ ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MID
+ | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+ | ModemPowerProfile.MODEM_TX_LEVEL_0));
+ assertEquals(35.0, mpp.getAverageBatteryDrainMa(
+ ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MID
+ | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+ | ModemPowerProfile.MODEM_TX_LEVEL_1));
+ assertEquals(36.0, mpp.getAverageBatteryDrainMa(
+ ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MID
+ | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+ | ModemPowerProfile.MODEM_TX_LEVEL_2));
+ assertEquals(37.0, mpp.getAverageBatteryDrainMa(
+ ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MID
+ | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+ | ModemPowerProfile.MODEM_TX_LEVEL_3));
+ assertEquals(38.0, mpp.getAverageBatteryDrainMa(
+ ModemPowerProfile.MODEM_RAT_TYPE_NR | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MID
+ | ModemPowerProfile.MODEM_DRAIN_TYPE_TX
+ | ModemPowerProfile.MODEM_TX_LEVEL_4));
+
+ assertEquals(43.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+ | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_HIGH
+ | ModemPowerProfile.MODEM_DRAIN_TYPE_RX));
+ assertEquals(44.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+ | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_HIGH
+ | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_0));
+ assertEquals(45.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+ | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_HIGH
+ | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_1));
+ assertEquals(46.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+ | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_HIGH
+ | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_2));
+ assertEquals(47.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+ | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_HIGH
+ | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_3));
+ assertEquals(48.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+ | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_HIGH
+ | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_4));
+
+ assertEquals(53.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+ | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MMWAVE
+ | ModemPowerProfile.MODEM_DRAIN_TYPE_RX));
+ assertEquals(54.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+ | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MMWAVE
+ | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_0));
+ assertEquals(55.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+ | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MMWAVE
+ | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_1));
+ assertEquals(56.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+ | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MMWAVE
+ | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_2));
+ assertEquals(57.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+ | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MMWAVE
+ | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_3));
+ assertEquals(58.0, mpp.getAverageBatteryDrainMa(ModemPowerProfile.MODEM_RAT_TYPE_NR
+ | ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MMWAVE
+ | ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_4));
+ }
+
+ private XmlResourceParser getTestModemElement(@XmlRes int xmlId, String elementName)
+ throws Exception {
+ final String element = TAG_TEST_MODEM;
+ final Resources resources = mContext.getResources();
+ XmlResourceParser parser = resources.getXml(xmlId);
+ while (true) {
+ XmlUtils.nextElement(parser);
+ final String e = parser.getName();
+ if (e == null) break;
+ if (!e.equals(element)) continue;
+
+ final String name = parser.getAttributeValue(null, ATTR_NAME);
+ if (!name.equals(elementName)) continue;
+
+ return parser;
+ }
+ fail("Unanable to find element " + element + " with name " + elementName);
+ return null;
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java
index a7873576e728..a36839910742 100644
--- a/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java
@@ -92,7 +92,10 @@ public class WifiPowerCalculatorTest {
final BatteryStatsImpl batteryStats = setupTestNetworkNumbers();
final WifiActivityEnergyInfo energyInfo = setupPowerControllerBasedModelEnergyNumbersInfo();
- batteryStats.updateWifiState(energyInfo, POWER_DATA_UNAVAILABLE, 1000, 1000,
+ batteryStats.noteWifiScanStartedLocked(APP_UID, 500, 500);
+ batteryStats.noteWifiScanStoppedLocked(APP_UID, 1500, 1500);
+
+ batteryStats.updateWifiState(energyInfo, POWER_DATA_UNAVAILABLE, 2000, 2000,
mNetworkStatsManager);
WifiPowerCalculator calculator = new WifiPowerCalculator(mStatsRule.getPowerProfile());
@@ -100,15 +103,15 @@ public class WifiPowerCalculatorTest {
UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID);
assertThat(uidConsumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_WIFI))
- .isEqualTo(1423);
+ .isEqualTo(2473);
assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI))
- .isWithin(PRECISION).of(0.2214666);
+ .isWithin(PRECISION).of(0.3964);
assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_WIFI))
.isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
BatteryConsumer deviceConsumer = mStatsRule.getDeviceBatteryConsumer();
assertThat(deviceConsumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_WIFI))
- .isEqualTo(4002);
+ .isEqualTo(4001);
assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI))
.isWithin(PRECISION).of(0.86666);
assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_WIFI))
diff --git a/data/etc/com.android.settings.xml b/data/etc/com.android.settings.xml
index 3fdb0da80875..ddcab6eb76c8 100644
--- a/data/etc/com.android.settings.xml
+++ b/data/etc/com.android.settings.xml
@@ -30,6 +30,7 @@
<permission name="android.permission.MANAGE_DEBUGGING"/>
<permission name="android.permission.MANAGE_DEVICE_ADMINS"/>
<permission name="android.permission.MANAGE_FINGERPRINT"/>
+ <permission name="android.permission.MANAGE_GAME_MODE" />
<permission name="android.permission.MANAGE_USB"/>
<permission name="android.permission.MANAGE_USERS"/>
<permission name="android.permission.MANAGE_USER_OEM_UNLOCK_STATE" />
@@ -59,5 +60,6 @@
<permission name="android.permission.READ_DREAM_STATE"/>
<permission name="android.permission.READ_DREAM_SUPPRESSION"/>
<permission name="android.permission.RESTART_WIFI_SUBSYSTEM"/>
+ <permission name="android.permission.READ_SAFETY_CENTER_STATUS" />
</privapp-permissions>
</permissions>
diff --git a/data/etc/com.android.systemui.xml b/data/etc/com.android.systemui.xml
index f2a33de008d6..d95644a02e69 100644
--- a/data/etc/com.android.systemui.xml
+++ b/data/etc/com.android.systemui.xml
@@ -30,6 +30,7 @@
<permission name="android.permission.GET_APP_OPS_STATS"/>
<permission name="android.permission.INTERACT_ACROSS_USERS"/>
<permission name="android.permission.MANAGE_DEBUGGING"/>
+ <permission name="android.permission.MANAGE_GAME_MODE" />
<permission name="android.permission.MANAGE_SENSOR_PRIVACY"/>
<permission name="android.permission.MANAGE_USB"/>
<permission name="android.permission.MANAGE_USERS"/>
@@ -50,6 +51,7 @@
<permission name="android.permission.REAL_GET_TASKS"/>
<permission name="android.permission.REQUEST_NETWORK_SCORES"/>
<permission name="android.permission.RECEIVE_MEDIA_RESOURCE_USAGE"/>
+ <permission name="android.permission.SET_WALLPAPER_DIM_AMOUNT"/>
<permission name="android.permission.START_ACTIVITIES_FROM_BACKGROUND" />
<permission name="android.permission.START_ACTIVITY_AS_CALLER"/>
<permission name="android.permission.START_TASKS_FROM_RECENTS"/>
@@ -71,5 +73,6 @@
<permission name="android.permission.USE_BACKGROUND_BLUR" />
<permission name="android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS" />
<permission name="android.permission.FORCE_STOP_PACKAGES" />
+ <permission name="android.permission.ACCESS_FPS_COUNTER" />
</privapp-permissions>
</permissions>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 6f5951bdaca6..de086df13722 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -333,6 +333,7 @@ applications that come with the platform
<permission name="android.permission.LOCAL_MAC_ADDRESS"/>
<permission name="android.permission.MANAGE_ACCESSIBILITY"/>
<permission name="android.permission.MANAGE_DEVICE_ADMINS"/>
+ <permission name="android.permission.MANAGE_GAME_MODE"/>
<permission name="android.permission.MANAGE_ROLLBACKS"/>
<permission name="android.permission.MANAGE_USB"/>
<permission name="android.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS"/>
@@ -394,6 +395,9 @@ applications that come with the platform
<permission name="android.permission.SET_WALLPAPER_COMPONENT" />
<permission name="android.permission.SET_WALLPAPER_DIM_AMOUNT" />
<permission name="android.permission.REQUEST_NOTIFICATION_ASSISTANT_SERVICE" />
+ <!-- Permission required for CTS test - TrustTestCases -->
+ <permission name="android.permission.PROVIDE_TRUST_AGENT" />
+ <permission name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE" />
<!-- Permissions required for Incremental CTS tests -->
<permission name="com.android.permission.USE_INSTALLER_V2"/>
<permission name="android.permission.LOADER_USAGE_STATS"/>
@@ -571,6 +575,7 @@ applications that come with the platform
<privapp-permissions package="com.android.settings">
<permission name="android.permission.INSTALL_DYNAMIC_SYSTEM"/>
<permission name="android.permission.BIND_CELL_BROADCAST_SERVICE"/>
+ <permission name="android.permission.READ_SAFETY_CENTER_STATUS" />
</privapp-permissions>
<privapp-permissions package="com.android.bips">
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 535d656462f4..07523299c353 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -103,18 +103,6 @@
"group": "WM_DEBUG_STATES",
"at": "com\/android\/server\/wm\/TaskFragment.java"
},
- "-2002500255": {
- "message": "Defer removing snapshot surface in %dms",
- "level": "VERBOSE",
- "group": "WM_DEBUG_STARTING_WINDOW",
- "at": "com\/android\/server\/wm\/TaskSnapshotSurface.java"
- },
- "-1991255017": {
- "message": "Drawing snapshot surface sizeMismatch=%b",
- "level": "VERBOSE",
- "group": "WM_DEBUG_STARTING_WINDOW",
- "at": "com\/android\/server\/wm\/TaskSnapshotSurface.java"
- },
"-1980468143": {
"message": "DisplayArea appeared name=%s",
"level": "VERBOSE",
@@ -505,12 +493,6 @@
"group": "WM_DEBUG_STATES",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
- "-1556507536": {
- "message": "Passing transform hint %d for window %s%s",
- "level": "VERBOSE",
- "group": "WM_DEBUG_ORIENTATION",
- "at": "com\/android\/server\/wm\/WindowManagerService.java"
- },
"-1554521902": {
"message": "showInsets(ime) was requested by different window: %s ",
"level": "WARN",
@@ -745,6 +727,12 @@
"group": "WM_DEBUG_BOOT",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "-1343787701": {
+ "message": "startBackNavigation task=%s, topRunningActivity=%s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_BACK_PREVIEW",
+ "at": "com\/android\/server\/wm\/BackNavigationController.java"
+ },
"-1340540100": {
"message": "Creating SnapshotStartingData",
"level": "VERBOSE",
@@ -1597,12 +1585,6 @@
"group": "WM_DEBUG_FOCUS_LIGHT",
"at": "com\/android\/server\/wm\/DisplayContent.java"
},
- "-405536909": {
- "message": "Removing snapshot surface",
- "level": "VERBOSE",
- "group": "WM_DEBUG_STARTING_WINDOW",
- "at": "com\/android\/server\/wm\/TaskSnapshotSurface.java"
- },
"-401282500": {
"message": "destroyIfPossible: r=%s destroy returned removed=%s",
"level": "DEBUG",
@@ -1867,6 +1849,12 @@
"group": "WM_DEBUG_STATES",
"at": "com\/android\/server\/wm\/Task.java"
},
+ "-134091882": {
+ "message": "Screenshotting Activity %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_BACK_PREVIEW",
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
+ },
"-124316973": {
"message": "Translucent=%s Floating=%s ShowWallpaper=%s Disable=%s",
"level": "VERBOSE",
@@ -1951,6 +1939,12 @@
"group": "WM_DEBUG_APP_TRANSITIONS_ANIM",
"at": "com\/android\/server\/wm\/WindowContainer.java"
},
+ "-23020844": {
+ "message": "Back: Reset surfaces",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_BACK_PREVIEW",
+ "at": "com\/android\/server\/wm\/BackNavigationController.java"
+ },
"-21399771": {
"message": "activity %s already destroying, skipping request with reason:%s",
"level": "VERBOSE",
@@ -2005,12 +1999,6 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
- "44438983": {
- "message": "performLayout: Activity exiting now removed %s",
- "level": "VERBOSE",
- "group": "WM_DEBUG_ADD_REMOVE",
- "at": "com\/android\/server\/wm\/DisplayContent.java"
- },
"45285419": {
"message": "startingWindow was set but startingSurface==null, couldn't remove",
"level": "VERBOSE",
@@ -2767,6 +2755,12 @@
"group": "WM_SHOW_SURFACE_ALLOC",
"at": "com\/android\/server\/wm\/WindowStateAnimator.java"
},
+ "751854538": {
+ "message": "DisplayArea keep clear rects changed name =%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_WINDOW_ORGANIZER",
+ "at": "com\/android\/server\/wm\/DisplayAreaOrganizerController.java"
+ },
"765395228": {
"message": "onAnimationFinished(): controller=%s reorderMode=%d",
"level": "DEBUG",
@@ -3271,12 +3265,6 @@
"group": "WM_DEBUG_LAYER_MIRRORING",
"at": "com\/android\/server\/wm\/DisplayContent.java"
},
- "1417601133": {
- "message": "Enqueueing ADD_STARTING",
- "level": "VERBOSE",
- "group": "WM_DEBUG_STARTING_WINDOW",
- "at": "com\/android\/server\/wm\/ActivityRecord.java"
- },
"1422781269": {
"message": "Resuming rotation after re-position",
"level": "DEBUG",
@@ -3397,6 +3385,12 @@
"group": "WM_DEBUG_APP_TRANSITIONS",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
+ "1554795024": {
+ "message": "Previous Activity is %s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_BACK_PREVIEW",
+ "at": "com\/android\/server\/wm\/BackNavigationController.java"
+ },
"1557732761": {
"message": "For Intent %s bringing to top: %s",
"level": "DEBUG",
@@ -3697,12 +3691,6 @@
"group": "WM_DEBUG_WINDOW_ORGANIZER",
"at": "com\/android\/server\/wm\/DisplayAreaPolicyBuilder.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",
@@ -3924,6 +3912,9 @@
"WM_DEBUG_APP_TRANSITIONS_ANIM": {
"tag": "WindowManager"
},
+ "WM_DEBUG_BACK_PREVIEW": {
+ "tag": "CoreBackPreview"
+ },
"WM_DEBUG_BOOT": {
"tag": "WindowManager"
},
diff --git a/graphics/java/android/graphics/text/LineBreakConfig.java b/graphics/java/android/graphics/text/LineBreakConfig.java
index 4d818583fd23..cffdf28dbc27 100644
--- a/graphics/java/android/graphics/text/LineBreakConfig.java
+++ b/graphics/java/android/graphics/text/LineBreakConfig.java
@@ -17,7 +17,7 @@
package android.graphics.text;
import android.annotation.IntDef;
-import android.annotation.Nullable;
+import android.annotation.NonNull;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -58,7 +58,28 @@ public final class LineBreakConfig {
@Retention(RetentionPolicy.SOURCE)
public @interface LineBreakStyle {}
+ /**
+ * No line break word style specified.
+ */
+ public static final int LINE_BREAK_WORD_STYLE_NONE = 0;
+
+ /**
+ * Indicates the line breaking is based on the phrased. This makes text wrapping only on
+ * meaningful words. The support of the text wrapping word style varies depending on the
+ * locales. If the locale does not support the phrase based text wrapping,
+ * there will be no effect.
+ */
+ public static final int LINE_BREAK_WORD_STYLE_PHRASE = 1;
+
+ /** @hide */
+ @IntDef(prefix = { "LINE_BREAK_WORD_STYLE_" }, value = {
+ LINE_BREAK_WORD_STYLE_NONE, LINE_BREAK_WORD_STYLE_PHRASE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface LineBreakWordStyle {}
+
private @LineBreakStyle int mLineBreakStyle = LINE_BREAK_STYLE_NONE;
+ private @LineBreakWordStyle int mLineBreakWordStyle = LINE_BREAK_WORD_STYLE_NONE;
public LineBreakConfig() {
}
@@ -66,14 +87,12 @@ public final class LineBreakConfig {
/**
* Set the line break configuration.
*
- * @param config the new line break configuration.
+ * @param lineBreakConfig the new line break configuration.
*/
- public void set(@Nullable LineBreakConfig config) {
- if (config != null) {
- mLineBreakStyle = config.getLineBreakStyle();
- } else {
- mLineBreakStyle = LineBreakConfig.LINE_BREAK_STYLE_NONE;
- }
+ public void set(@NonNull LineBreakConfig lineBreakConfig) {
+ Objects.requireNonNull(lineBreakConfig);
+ mLineBreakStyle = lineBreakConfig.getLineBreakStyle();
+ mLineBreakWordStyle = lineBreakConfig.getLineBreakWordStyle();
}
/**
@@ -94,17 +113,36 @@ public final class LineBreakConfig {
mLineBreakStyle = lineBreakStyle;
}
+ /**
+ * Get the line break word style.
+ *
+ * @return The current line break word style to be used for the text wrapping.
+ */
+ public @LineBreakWordStyle int getLineBreakWordStyle() {
+ return mLineBreakWordStyle;
+ }
+
+ /**
+ * Set the line break word style.
+ *
+ * @param lineBreakWordStyle the new line break word style.
+ */
+ public void setLineBreakWordStyle(@LineBreakWordStyle int lineBreakWordStyle) {
+ mLineBreakWordStyle = lineBreakWordStyle;
+ }
+
@Override
public boolean equals(Object o) {
if (o == null) return false;
if (this == o) return true;
if (!(o instanceof LineBreakConfig)) return false;
LineBreakConfig that = (LineBreakConfig) o;
- return mLineBreakStyle == that.mLineBreakStyle;
+ return (mLineBreakStyle == that.mLineBreakStyle)
+ && (mLineBreakWordStyle == that.mLineBreakWordStyle);
}
@Override
public int hashCode() {
- return Objects.hash(mLineBreakStyle);
+ return Objects.hash(mLineBreakStyle, mLineBreakWordStyle);
}
}
diff --git a/graphics/java/android/graphics/text/MeasuredText.java b/graphics/java/android/graphics/text/MeasuredText.java
index 5f4afb7b9888..6d691c122576 100644
--- a/graphics/java/android/graphics/text/MeasuredText.java
+++ b/graphics/java/android/graphics/text/MeasuredText.java
@@ -264,8 +264,10 @@ public class MeasuredText {
Preconditions.checkArgument(end <= mText.length, "Style exceeds the text length");
int lbStyle = (lineBreakConfig != null) ? lineBreakConfig.getLineBreakStyle() :
LineBreakConfig.LINE_BREAK_STYLE_NONE;
- nAddStyleRun(mNativePtr, paint.getNativeInstance(), lbStyle, mCurrentOffset, end,
- isRtl);
+ int lbWordStyle = (lineBreakConfig != null) ? lineBreakConfig.getLineBreakWordStyle() :
+ LineBreakConfig.LINE_BREAK_WORD_STYLE_NONE;
+ nAddStyleRun(mNativePtr, paint.getNativeInstance(), lbStyle, lbWordStyle,
+ mCurrentOffset, end, isRtl);
mCurrentOffset = end;
return this;
}
@@ -445,7 +447,8 @@ public class MeasuredText {
*
* @param nativeBuilderPtr The native MeasuredParagraph builder pointer.
* @param paintPtr The native paint pointer to be applied.
- * @param lineBreakStyle The line break style of the text.
+ * @param lineBreakStyle The line break style(lb) of the text.
+ * @param lineBreakWordStyle The line break word style(lw) of the text.
* @param start The start offset in the copied buffer.
* @param end The end offset in the copied buffer.
* @param isRtl True if the text is RTL.
@@ -453,6 +456,7 @@ public class MeasuredText {
private static native void nAddStyleRun(/* Non Zero */ long nativeBuilderPtr,
/* Non Zero */ long paintPtr,
int lineBreakStyle,
+ int lineBreakWordStyle,
@IntRange(from = 0) int start,
@IntRange(from = 0) int end,
boolean isRtl);
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index a9543443d3f4..8811a7fec932 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -20,7 +20,6 @@ import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.UserHandle;
import android.security.maintenance.UserState;
-import android.system.keystore2.Domain;
/**
* @hide This should not be made public in its present form because it
@@ -120,15 +119,6 @@ public class KeyStore {
}
/**
- * Forwards the request to clear a UID to Keystore 2.0.
- * @hide
- */
- public boolean clearUid(int uid) {
- return AndroidKeyStoreMaintenance.clearNamespace(Domain.APP, uid) == 0;
- }
-
-
- /**
* Add an authentication record to the keystore authorization table.
*
* @param authToken The packed bytes of a hw_auth_token_t to be provided to keymaster.
diff --git a/libs/WindowManager/Shell/res/values-af/strings_tv.xml b/libs/WindowManager/Shell/res/values-af/strings_tv.xml
index 6ce588034f9e..3edb8e967768 100644
--- a/libs/WindowManager/Shell/res/values-af/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-af/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Titellose program)"</string>
<string name="pip_close" msgid="9135220303720555525">"Maak PIP toe"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Volskerm"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-am/strings_tv.xml b/libs/WindowManager/Shell/res/values-am/strings_tv.xml
index fcb87c5682e3..b1c6542ce616 100644
--- a/libs/WindowManager/Shell/res/values-am/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-am/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(ርዕስ የሌለው ፕሮግራም)"</string>
<string name="pip_close" msgid="9135220303720555525">"PIPን ዝጋ"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"ሙሉ ማያ ገጽ"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ar/strings_tv.xml b/libs/WindowManager/Shell/res/values-ar/strings_tv.xml
index 4eef29e2ed12..dfc505365ca4 100644
--- a/libs/WindowManager/Shell/res/values-ar/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ar/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(ليس هناك عنوان للبرنامج)"</string>
<string name="pip_close" msgid="9135220303720555525">"‏إغلاق PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"ملء الشاشة"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-as/strings_tv.xml b/libs/WindowManager/Shell/res/values-as/strings_tv.xml
index 170b2dbd458c..352bde5a1931 100644
--- a/libs/WindowManager/Shell/res/values-as/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-as/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(শিৰোনামবিহীন কাৰ্যক্ৰম)"</string>
<string name="pip_close" msgid="9135220303720555525">"পিপ বন্ধ কৰক"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"সম্পূৰ্ণ স্ক্ৰীন"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-az/strings_tv.xml b/libs/WindowManager/Shell/res/values-az/strings_tv.xml
index c9f1acbef31b..9b46d5fc222c 100644
--- a/libs/WindowManager/Shell/res/values-az/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-az/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Başlıqsız proqram)"</string>
<string name="pip_close" msgid="9135220303720555525">"PIP bağlayın"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Tam ekran"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings_tv.xml b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings_tv.xml
index 6fbc91bbec60..790a6d471e55 100644
--- a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Program bez naslova)"</string>
<string name="pip_close" msgid="9135220303720555525">"Zatvori PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Ceo ekran"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-be/strings_tv.xml b/libs/WindowManager/Shell/res/values-be/strings_tv.xml
index d33bf99e2ebd..c4df7fc74121 100644
--- a/libs/WindowManager/Shell/res/values-be/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-be/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Праграма без назвы)"</string>
<string name="pip_close" msgid="9135220303720555525">"Закрыць PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Поўнаэкранны рэжым"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-bg/strings_tv.xml b/libs/WindowManager/Shell/res/values-bg/strings_tv.xml
index f4fad601179f..cbb00ae19024 100644
--- a/libs/WindowManager/Shell/res/values-bg/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-bg/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Програма без заглавие)"</string>
<string name="pip_close" msgid="9135220303720555525">"Затваряне на PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Цял екран"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-bn/strings_tv.xml b/libs/WindowManager/Shell/res/values-bn/strings_tv.xml
index 0eb83a0276e6..f24c92a797e5 100644
--- a/libs/WindowManager/Shell/res/values-bn/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-bn/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(শিরোনামহীন প্রোগ্রাম)"</string>
<string name="pip_close" msgid="9135220303720555525">"PIP বন্ধ করুন"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"পূর্ণ স্ক্রিন"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-bs/strings_tv.xml b/libs/WindowManager/Shell/res/values-bs/strings_tv.xml
index 9a655bb41066..80bac2a07da3 100644
--- a/libs/WindowManager/Shell/res/values-bs/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-bs/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Program bez naslova)"</string>
<string name="pip_close" msgid="9135220303720555525">"Zatvori sliku u slici"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Cijeli ekran"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ca/strings_tv.xml b/libs/WindowManager/Shell/res/values-ca/strings_tv.xml
index b80fc41402dd..66cd93a2c78e 100644
--- a/libs/WindowManager/Shell/res/values-ca/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ca/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Programa sense títol)"</string>
<string name="pip_close" msgid="9135220303720555525">"Tanca PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Pantalla completa"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-cs/strings_tv.xml b/libs/WindowManager/Shell/res/values-cs/strings_tv.xml
index 56abcbe473fb..500050b09cab 100644
--- a/libs/WindowManager/Shell/res/values-cs/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-cs/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Bez názvu)"</string>
<string name="pip_close" msgid="9135220303720555525">"Ukončit obraz v obraze (PIP)"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Celá obrazovka"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-da/strings_tv.xml b/libs/WindowManager/Shell/res/values-da/strings_tv.xml
index fdb6b783399e..896895b90f47 100644
--- a/libs/WindowManager/Shell/res/values-da/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-da/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Program uden titel)"</string>
<string name="pip_close" msgid="9135220303720555525">"Luk integreret billede"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Fuld skærm"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-de/strings_tv.xml b/libs/WindowManager/Shell/res/values-de/strings_tv.xml
index 02cce9d73647..7809efe85afa 100644
--- a/libs/WindowManager/Shell/res/values-de/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-de/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Kein Sendungsname gefunden)"</string>
<string name="pip_close" msgid="9135220303720555525">"PIP schließen"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Vollbild"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-el/strings_tv.xml b/libs/WindowManager/Shell/res/values-el/strings_tv.xml
index 880ea37e6bf7..088bcc39b859 100644
--- a/libs/WindowManager/Shell/res/values-el/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-el/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Δεν υπάρχει τίτλος προγράμματος)"</string>
<string name="pip_close" msgid="9135220303720555525">"Κλείσιμο PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Πλήρης οθόνη"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rAU/strings_tv.xml b/libs/WindowManager/Shell/res/values-en-rAU/strings_tv.xml
index e3f08c8cc76f..7900fdc3a0b1 100644
--- a/libs/WindowManager/Shell/res/values-en-rAU/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-en-rAU/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(No title program)"</string>
<string name="pip_close" msgid="9135220303720555525">"Close PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Full screen"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rCA/strings_tv.xml b/libs/WindowManager/Shell/res/values-en-rCA/strings_tv.xml
index e3f08c8cc76f..7900fdc3a0b1 100644
--- a/libs/WindowManager/Shell/res/values-en-rCA/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-en-rCA/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(No title program)"</string>
<string name="pip_close" msgid="9135220303720555525">"Close PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Full screen"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rGB/strings_tv.xml b/libs/WindowManager/Shell/res/values-en-rGB/strings_tv.xml
index e3f08c8cc76f..7900fdc3a0b1 100644
--- a/libs/WindowManager/Shell/res/values-en-rGB/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-en-rGB/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(No title program)"</string>
<string name="pip_close" msgid="9135220303720555525">"Close PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Full screen"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rIN/strings_tv.xml b/libs/WindowManager/Shell/res/values-en-rIN/strings_tv.xml
index e3f08c8cc76f..7900fdc3a0b1 100644
--- a/libs/WindowManager/Shell/res/values-en-rIN/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-en-rIN/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(No title program)"</string>
<string name="pip_close" msgid="9135220303720555525">"Close PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Full screen"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rXC/strings_tv.xml b/libs/WindowManager/Shell/res/values-en-rXC/strings_tv.xml
index 3f9ef0ea2816..3b12d90f33a2 100644
--- a/libs/WindowManager/Shell/res/values-en-rXC/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-en-rXC/strings_tv.xml
@@ -21,4 +21,5 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‏‏‏‏‎‎‎‏‎‎‏‏‏‎‎‏‎‏‎‎‎‏‏‏‏‎‏‏‎‎‏‎‏‏‎‎‏‏‏‏‏‎‏‎‏‎‏‎‎‎‏‎‏‎‏‏‏‎(No title program)‎‏‎‎‏‎"</string>
<string name="pip_close" msgid="9135220303720555525">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‎‎‏‏‎‏‏‎‏‎‎‏‎‎‏‏‏‎‏‏‏‎‎‏‏‏‏‎‎‎‎‏‏‎‎‏‏‎‎‏‎‎‏‎‎‎‎‎‎‎‏‎‏‎Close PIP‎‏‎‎‏‎"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‏‎‎‎‎‎‎‎‎‏‏‎‏‎‎‎‏‏‎‎‎‎‎‎‏‏‏‏‎‎‎‎‏‎‏‎‎‏‎‎‎‎‎‎‎‏‎‎‏‏‎‎‏‏‎‏‎‎Full screen‎‏‎‎‏‎"</string>
+ <string name="pip_move" msgid="1544227837964635439">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‏‎‏‏‎‏‏‏‎‎‎‏‏‎‎‏‏‎‎‎‎‏‎‎‏‎‏‏‏‎‏‏‎‎‎‏‎‎‏‏‎‏‏‎‎‏‏‎‏‎‎‏‎‏‏‏‏‎Move PIP‎‏‎‎‏‎"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-es-rUS/strings_tv.xml b/libs/WindowManager/Shell/res/values-es-rUS/strings_tv.xml
index 5d5954a19761..3be850a90c30 100644
--- a/libs/WindowManager/Shell/res/values-es-rUS/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-es-rUS/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Sin título de programa)"</string>
<string name="pip_close" msgid="9135220303720555525">"Cerrar PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Pantalla completa"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-es/strings_tv.xml b/libs/WindowManager/Shell/res/values-es/strings_tv.xml
index d31b9b45cae3..7eba361df2c3 100644
--- a/libs/WindowManager/Shell/res/values-es/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-es/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Programa sin título)"</string>
<string name="pip_close" msgid="9135220303720555525">"Cerrar PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Pantalla completa"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-et/strings_tv.xml b/libs/WindowManager/Shell/res/values-et/strings_tv.xml
index bc7a6adafc03..ca6e6695b52b 100644
--- a/libs/WindowManager/Shell/res/values-et/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-et/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Programmi pealkiri puudub)"</string>
<string name="pip_close" msgid="9135220303720555525">"Sule PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Täisekraan"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-eu/strings_tv.xml b/libs/WindowManager/Shell/res/values-eu/strings_tv.xml
index cf5f98883082..3f47e9591f66 100644
--- a/libs/WindowManager/Shell/res/values-eu/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-eu/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Programa izengabea)"</string>
<string name="pip_close" msgid="9135220303720555525">"Itxi PIPa"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Pantaila osoa"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-fa/strings_tv.xml b/libs/WindowManager/Shell/res/values-fa/strings_tv.xml
index 5b815b4c7b86..cc5cf64d01dd 100644
--- a/libs/WindowManager/Shell/res/values-fa/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-fa/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(برنامه بدون عنوان)"</string>
<string name="pip_close" msgid="9135220303720555525">"‏بستن PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"تمام صفحه"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-fi/strings_tv.xml b/libs/WindowManager/Shell/res/values-fi/strings_tv.xml
index 77ad6eef91e7..b77988659cc3 100644
--- a/libs/WindowManager/Shell/res/values-fi/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-fi/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Nimetön)"</string>
<string name="pip_close" msgid="9135220303720555525">"Sulje PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Koko näyttö"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-fr-rCA/strings_tv.xml b/libs/WindowManager/Shell/res/values-fr-rCA/strings_tv.xml
index 0ec7f40f0e9f..1798c7db482c 100644
--- a/libs/WindowManager/Shell/res/values-fr-rCA/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-fr-rCA/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Aucun programme de titre)"</string>
<string name="pip_close" msgid="9135220303720555525">"Fermer mode IDI"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Plein écran"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-fr/strings_tv.xml b/libs/WindowManager/Shell/res/values-fr/strings_tv.xml
index 27fd155535b7..b039934ada8c 100644
--- a/libs/WindowManager/Shell/res/values-fr/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-fr/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Programme sans titre)"</string>
<string name="pip_close" msgid="9135220303720555525">"Fermer mode PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Plein écran"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-gl/strings_tv.xml b/libs/WindowManager/Shell/res/values-gl/strings_tv.xml
index df96f6cb794d..0d91eba31492 100644
--- a/libs/WindowManager/Shell/res/values-gl/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-gl/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Programa sen título)"</string>
<string name="pip_close" msgid="9135220303720555525">"Pechar PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Pantalla completa"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-gu/strings_tv.xml b/libs/WindowManager/Shell/res/values-gu/strings_tv.xml
index 3608f1d530c0..a748df3e2f43 100644
--- a/libs/WindowManager/Shell/res/values-gu/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-gu/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(કોઈ ટાઇટલ પ્રોગ્રામ નથી)"</string>
<string name="pip_close" msgid="9135220303720555525">"PIP બંધ કરો"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"પૂર્ણ સ્ક્રીન"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-hi/strings_tv.xml b/libs/WindowManager/Shell/res/values-hi/strings_tv.xml
index 720bb6ca5e24..040072bf5087 100644
--- a/libs/WindowManager/Shell/res/values-hi/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-hi/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(कोई शीर्षक कार्यक्रम नहीं)"</string>
<string name="pip_close" msgid="9135220303720555525">"PIP बंद करें"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"फ़ुल स्‍क्रीन"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-hr/strings_tv.xml b/libs/WindowManager/Shell/res/values-hr/strings_tv.xml
index 21f8cb63f470..20081e4b49dc 100644
--- a/libs/WindowManager/Shell/res/values-hr/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-hr/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Program bez naslova)"</string>
<string name="pip_close" msgid="9135220303720555525">"Zatvori PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Cijeli zaslon"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-hu/strings_tv.xml b/libs/WindowManager/Shell/res/values-hu/strings_tv.xml
index 0010086bb0b5..c78146d9a773 100644
--- a/libs/WindowManager/Shell/res/values-hu/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-hu/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Cím nélküli program)"</string>
<string name="pip_close" msgid="9135220303720555525">"PIP bezárása"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Teljes képernyő"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-hy/strings_tv.xml b/libs/WindowManager/Shell/res/values-hy/strings_tv.xml
index cb18762be48b..55d5bd76ecd3 100644
--- a/libs/WindowManager/Shell/res/values-hy/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-hy/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Առանց վերնագրի ծրագիր)"</string>
<string name="pip_close" msgid="9135220303720555525">"Փակել PIP-ն"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Լիէկրան"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-in/strings_tv.xml b/libs/WindowManager/Shell/res/values-in/strings_tv.xml
index 8f3a28764b00..640185263f8e 100644
--- a/libs/WindowManager/Shell/res/values-in/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-in/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Program tanpa judul)"</string>
<string name="pip_close" msgid="9135220303720555525">"Tutup PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Layar penuh"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-is/strings_tv.xml b/libs/WindowManager/Shell/res/values-is/strings_tv.xml
index 1f148d948a0e..fa36829f5634 100644
--- a/libs/WindowManager/Shell/res/values-is/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-is/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Efni án titils)"</string>
<string name="pip_close" msgid="9135220303720555525">"Loka mynd í mynd"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Allur skjárinn"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-it/strings_tv.xml b/libs/WindowManager/Shell/res/values-it/strings_tv.xml
index 127454cf28bf..f6e91be5b4ae 100644
--- a/libs/WindowManager/Shell/res/values-it/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-it/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Programma senza titolo)"</string>
<string name="pip_close" msgid="9135220303720555525">"Chiudi PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Schermo intero"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-iw/strings_tv.xml b/libs/WindowManager/Shell/res/values-iw/strings_tv.xml
index ef98a9c41cf2..356e8d536e4c 100644
--- a/libs/WindowManager/Shell/res/values-iw/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-iw/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(תוכנית ללא כותרת)"</string>
<string name="pip_close" msgid="9135220303720555525">"‏סגירת PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"מסך מלא"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ja/strings_tv.xml b/libs/WindowManager/Shell/res/values-ja/strings_tv.xml
index b7ab28c44fd2..07684d3acbe6 100644
--- a/libs/WindowManager/Shell/res/values-ja/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ja/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(無題の番組)"</string>
<string name="pip_close" msgid="9135220303720555525">"PIP を閉じる"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"全画面表示"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ka/strings_tv.xml b/libs/WindowManager/Shell/res/values-ka/strings_tv.xml
index 1bf4b8ebdcda..043e5eb8fda9 100644
--- a/libs/WindowManager/Shell/res/values-ka/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ka/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(პროგრამის სათაურის გარეშე)"</string>
<string name="pip_close" msgid="9135220303720555525">"PIP-ის დახურვა"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"სრულ ეკრანზე"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-kk/strings_tv.xml b/libs/WindowManager/Shell/res/values-kk/strings_tv.xml
index 8f1e725e79e2..79437975ca7e 100644
--- a/libs/WindowManager/Shell/res/values-kk/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-kk/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Атаусыз бағдарлама)"</string>
<string name="pip_close" msgid="9135220303720555525">"PIP жабу"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Толық экран"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-km/strings_tv.xml b/libs/WindowManager/Shell/res/values-km/strings_tv.xml
index b55997056e66..2e5625407ae9 100644
--- a/libs/WindowManager/Shell/res/values-km/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-km/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(កម្មវិធី​គ្មានចំណងជើង)"</string>
<string name="pip_close" msgid="9135220303720555525">"បិទ PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"ពេញអេក្រង់"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-kn/strings_tv.xml b/libs/WindowManager/Shell/res/values-kn/strings_tv.xml
index 9d3942fa4dd3..6c8880d46cef 100644
--- a/libs/WindowManager/Shell/res/values-kn/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-kn/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(ಶೀರ್ಷಿಕೆ ರಹಿತ ಕಾರ್ಯಕ್ರಮ)"</string>
<string name="pip_close" msgid="9135220303720555525">"PIP ಮುಚ್ಚಿ"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"ಪೂರ್ಣ ಪರದೆ"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ko/strings_tv.xml b/libs/WindowManager/Shell/res/values-ko/strings_tv.xml
index 46d6ad4e0b0f..35b1b19f629c 100644
--- a/libs/WindowManager/Shell/res/values-ko/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ko/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(제목 없는 프로그램)"</string>
<string name="pip_close" msgid="9135220303720555525">"PIP 닫기"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"전체화면"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ky/strings_tv.xml b/libs/WindowManager/Shell/res/values-ky/strings_tv.xml
index d5d1d7ef914e..72d70f056ce9 100644
--- a/libs/WindowManager/Shell/res/values-ky/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ky/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Аталышы жок программа)"</string>
<string name="pip_close" msgid="9135220303720555525">"PIP\'ти жабуу"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Толук экран"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-lo/strings_tv.xml b/libs/WindowManager/Shell/res/values-lo/strings_tv.xml
index f6362c120b9f..3604726b7b69 100644
--- a/libs/WindowManager/Shell/res/values-lo/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-lo/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(ໂປຣແກຣມບໍ່ມີຊື່)"</string>
<string name="pip_close" msgid="9135220303720555525">"ປິດ PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"ເຕັມໜ້າຈໍ"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-lt/strings_tv.xml b/libs/WindowManager/Shell/res/values-lt/strings_tv.xml
index e4695a05f038..fa5a4c4427bf 100644
--- a/libs/WindowManager/Shell/res/values-lt/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-lt/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Programa be pavadinimo)"</string>
<string name="pip_close" msgid="9135220303720555525">"Uždaryti PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Visas ekranas"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-lv/strings_tv.xml b/libs/WindowManager/Shell/res/values-lv/strings_tv.xml
index f2b037fbeeee..cafd43ac9ef4 100644
--- a/libs/WindowManager/Shell/res/values-lv/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-lv/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Programma bez nosaukuma)"</string>
<string name="pip_close" msgid="9135220303720555525">"Aizvērt PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Pilnekrāna režīms"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-mk/strings_tv.xml b/libs/WindowManager/Shell/res/values-mk/strings_tv.xml
index 25dc764f4d5e..b927b568301a 100644
--- a/libs/WindowManager/Shell/res/values-mk/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-mk/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Програма без наслов)"</string>
<string name="pip_close" msgid="9135220303720555525">"Затвори PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Цел екран"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ml/strings_tv.xml b/libs/WindowManager/Shell/res/values-ml/strings_tv.xml
index c74e0bbfaa5b..aef059fcec4f 100644
--- a/libs/WindowManager/Shell/res/values-ml/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ml/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(പേരില്ലാത്ത പ്രോഗ്രാം)"</string>
<string name="pip_close" msgid="9135220303720555525">"PIP അടയ്ക്കുക"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"പൂര്‍ണ്ണ സ്ക്രീന്‍"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-mn/strings_tv.xml b/libs/WindowManager/Shell/res/values-mn/strings_tv.xml
index 55519d462b69..7dfec68fc958 100644
--- a/libs/WindowManager/Shell/res/values-mn/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-mn/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Гарчиггүй хөтөлбөр)"</string>
<string name="pip_close" msgid="9135220303720555525">"PIP-г хаах"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Бүтэн дэлгэц"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-mr/strings_tv.xml b/libs/WindowManager/Shell/res/values-mr/strings_tv.xml
index ad2cfc6035c2..447cb7d26544 100644
--- a/libs/WindowManager/Shell/res/values-mr/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-mr/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(शीर्षक नसलेला कार्यक्रम)"</string>
<string name="pip_close" msgid="9135220303720555525">"PIP बंद करा"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"फुल स्क्रीन"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ms/strings_tv.xml b/libs/WindowManager/Shell/res/values-ms/strings_tv.xml
index b2d7214381ef..3a5584def102 100644
--- a/libs/WindowManager/Shell/res/values-ms/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ms/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Program tiada tajuk)"</string>
<string name="pip_close" msgid="9135220303720555525">"Tutup PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Skrin penuh"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-my/strings_tv.xml b/libs/WindowManager/Shell/res/values-my/strings_tv.xml
index c18d53932163..84ec0e5caf11 100644
--- a/libs/WindowManager/Shell/res/values-my/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-my/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(ခေါင်းစဉ်မဲ့ အစီအစဉ်)"</string>
<string name="pip_close" msgid="9135220303720555525">"PIP ကိုပိတ်ပါ"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"မျက်နှာပြင် အပြည့်"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-nb/strings_tv.xml b/libs/WindowManager/Shell/res/values-nb/strings_tv.xml
index 8a7f315606ad..78ec6db0bd98 100644
--- a/libs/WindowManager/Shell/res/values-nb/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-nb/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Program uten tittel)"</string>
<string name="pip_close" msgid="9135220303720555525">"Lukk PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Fullskjerm"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ne/strings_tv.xml b/libs/WindowManager/Shell/res/values-ne/strings_tv.xml
index 87fa3279f05e..4458a14f0a83 100644
--- a/libs/WindowManager/Shell/res/values-ne/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ne/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(शीर्षकविहीन कार्यक्रम)"</string>
<string name="pip_close" msgid="9135220303720555525">"PIP लाई बन्द गर्नुहोस्"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"फुल स्क्रिन"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-nl/strings_tv.xml b/libs/WindowManager/Shell/res/values-nl/strings_tv.xml
index df3809e5d6c6..d21515dc31f6 100644
--- a/libs/WindowManager/Shell/res/values-nl/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-nl/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Naamloos programma)"</string>
<string name="pip_close" msgid="9135220303720555525">"PIP sluiten"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Volledig scherm"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-or/strings_tv.xml b/libs/WindowManager/Shell/res/values-or/strings_tv.xml
index 295a5c4ee1ce..a6793502d0df 100644
--- a/libs/WindowManager/Shell/res/values-or/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-or/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(କୌଣସି ଟାଇଟଲ୍‍ ପ୍ରୋଗ୍ରାମ୍‍ ନାହିଁ)"</string>
<string name="pip_close" msgid="9135220303720555525">"PIP ବନ୍ଦ କରନ୍ତୁ"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"ପୂର୍ଣ୍ଣ ସ୍କ୍ରୀନ୍‍"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-pa/strings_tv.xml b/libs/WindowManager/Shell/res/values-pa/strings_tv.xml
index e32895a9a239..a0ff4f370f9f 100644
--- a/libs/WindowManager/Shell/res/values-pa/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-pa/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(ਸਿਰਲੇਖ-ਰਹਿਤ ਪ੍ਰੋਗਰਾਮ)"</string>
<string name="pip_close" msgid="9135220303720555525">"PIP ਬੰਦ ਕਰੋ"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"ਪੂਰੀ ਸਕ੍ਰੀਨ"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-pl/strings_tv.xml b/libs/WindowManager/Shell/res/values-pl/strings_tv.xml
index 286fd7b2ff0f..6320893389a6 100644
--- a/libs/WindowManager/Shell/res/values-pl/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-pl/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Program bez tytułu)"</string>
<string name="pip_close" msgid="9135220303720555525">"Zamknij PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Pełny ekran"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-pt-rBR/strings_tv.xml b/libs/WindowManager/Shell/res/values-pt-rBR/strings_tv.xml
index 57edcdf74cf4..fef9d470d87f 100644
--- a/libs/WindowManager/Shell/res/values-pt-rBR/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rBR/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(programa sem título)"</string>
<string name="pip_close" msgid="9135220303720555525">"Fechar PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Tela cheia"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-pt-rPT/strings_tv.xml b/libs/WindowManager/Shell/res/values-pt-rPT/strings_tv.xml
index 9372e0f637cb..461571f57ea7 100644
--- a/libs/WindowManager/Shell/res/values-pt-rPT/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rPT/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Sem título do programa)"</string>
<string name="pip_close" msgid="9135220303720555525">"Fechar PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Ecrã inteiro"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-pt/strings_tv.xml b/libs/WindowManager/Shell/res/values-pt/strings_tv.xml
index 57edcdf74cf4..fef9d470d87f 100644
--- a/libs/WindowManager/Shell/res/values-pt/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-pt/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(programa sem título)"</string>
<string name="pip_close" msgid="9135220303720555525">"Fechar PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Tela cheia"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ro/strings_tv.xml b/libs/WindowManager/Shell/res/values-ro/strings_tv.xml
index 9438e4955b68..80bf151939ce 100644
--- a/libs/WindowManager/Shell/res/values-ro/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ro/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Program fără titlu)"</string>
<string name="pip_close" msgid="9135220303720555525">"Închideți PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Ecran complet"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ru/strings_tv.xml b/libs/WindowManager/Shell/res/values-ru/strings_tv.xml
index 24785aa7e184..de5348aaff1e 100644
--- a/libs/WindowManager/Shell/res/values-ru/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ru/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Без названия)"</string>
<string name="pip_close" msgid="9135220303720555525">"\"Кадр в кадре\" – выйти"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Во весь экран"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-si/strings_tv.xml b/libs/WindowManager/Shell/res/values-si/strings_tv.xml
index 62ee6d4f44d2..047004048665 100644
--- a/libs/WindowManager/Shell/res/values-si/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-si/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(මාතෘකාවක් නැති වැඩසටහන)"</string>
<string name="pip_close" msgid="9135220303720555525">"PIP වසන්න"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"සම්පූර්ණ තිරය"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sk/strings_tv.xml b/libs/WindowManager/Shell/res/values-sk/strings_tv.xml
index a7a515cdc61c..41a432c5c649 100644
--- a/libs/WindowManager/Shell/res/values-sk/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-sk/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Program bez názvu)"</string>
<string name="pip_close" msgid="9135220303720555525">"Zavrieť režim PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Celá obrazovka"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sl/strings_tv.xml b/libs/WindowManager/Shell/res/values-sl/strings_tv.xml
index fe5c9ae5d2a8..de5605f91b64 100644
--- a/libs/WindowManager/Shell/res/values-sl/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-sl/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Program brez naslova)"</string>
<string name="pip_close" msgid="9135220303720555525">"Zapri način PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Celozaslonsko"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sq/strings_tv.xml b/libs/WindowManager/Shell/res/values-sq/strings_tv.xml
index 1d5583b2c826..08a640962401 100644
--- a/libs/WindowManager/Shell/res/values-sq/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-sq/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Program pa titull)"</string>
<string name="pip_close" msgid="9135220303720555525">"Mbyll PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Ekrani i plotë"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sr/strings_tv.xml b/libs/WindowManager/Shell/res/values-sr/strings_tv.xml
index 62ad1e8f6e69..f932928ad158 100644
--- a/libs/WindowManager/Shell/res/values-sr/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-sr/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Програм без наслова)"</string>
<string name="pip_close" msgid="9135220303720555525">"Затвори PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Цео екран"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sv/strings_tv.xml b/libs/WindowManager/Shell/res/values-sv/strings_tv.xml
index 74fb590c3e4d..1428fdbacf4e 100644
--- a/libs/WindowManager/Shell/res/values-sv/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-sv/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Namnlöst program)"</string>
<string name="pip_close" msgid="9135220303720555525">"Stäng PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Helskärm"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sw/strings_tv.xml b/libs/WindowManager/Shell/res/values-sw/strings_tv.xml
index cf0d8a9b3910..615209f9e9bd 100644
--- a/libs/WindowManager/Shell/res/values-sw/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-sw/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Programu isiyo na jina)"</string>
<string name="pip_close" msgid="9135220303720555525">"Funga PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Skrini nzima"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ta/strings_tv.xml b/libs/WindowManager/Shell/res/values-ta/strings_tv.xml
index 8bca46314e30..71c242c94d1a 100644
--- a/libs/WindowManager/Shell/res/values-ta/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ta/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(தலைப்பு இல்லை)"</string>
<string name="pip_close" msgid="9135220303720555525">"PIPஐ மூடு"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"முழுத்திரை"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-te/strings_tv.xml b/libs/WindowManager/Shell/res/values-te/strings_tv.xml
index 47489efbc4c2..f2dfb39cb99d 100644
--- a/libs/WindowManager/Shell/res/values-te/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-te/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(శీర్షిక లేని ప్రోగ్రామ్)"</string>
<string name="pip_close" msgid="9135220303720555525">"PIPని మూసివేయి"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"పూర్తి స్క్రీన్"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-th/strings_tv.xml b/libs/WindowManager/Shell/res/values-th/strings_tv.xml
index d3797e7c3cde..e810c885a898 100644
--- a/libs/WindowManager/Shell/res/values-th/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-th/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(ไม่มีชื่อรายการ)"</string>
<string name="pip_close" msgid="9135220303720555525">"ปิด PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"เต็มหน้าจอ"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-tl/strings_tv.xml b/libs/WindowManager/Shell/res/values-tl/strings_tv.xml
index b01c1115cd34..11d2953467b2 100644
--- a/libs/WindowManager/Shell/res/values-tl/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-tl/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Walang pamagat na programa)"</string>
<string name="pip_close" msgid="9135220303720555525">"Isara ang PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Full screen"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-tr/strings_tv.xml b/libs/WindowManager/Shell/res/values-tr/strings_tv.xml
index c92c4d02f465..6ed6e9fa1656 100644
--- a/libs/WindowManager/Shell/res/values-tr/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-tr/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Başlıksız program)"</string>
<string name="pip_close" msgid="9135220303720555525">"PIP\'yi kapat"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Tam ekran"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-uk/strings_tv.xml b/libs/WindowManager/Shell/res/values-uk/strings_tv.xml
index 74d4723d7850..482f59a4c117 100644
--- a/libs/WindowManager/Shell/res/values-uk/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-uk/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Програма без назви)"</string>
<string name="pip_close" msgid="9135220303720555525">"Закрити PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"На весь екран"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ur/strings_tv.xml b/libs/WindowManager/Shell/res/values-ur/strings_tv.xml
index 317953309947..c1954c750941 100644
--- a/libs/WindowManager/Shell/res/values-ur/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ur/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(بلا عنوان پروگرام)"</string>
<string name="pip_close" msgid="9135220303720555525">"‏PIP بند کریں"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"فُل اسکرین"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-uz/strings_tv.xml b/libs/WindowManager/Shell/res/values-uz/strings_tv.xml
index ae5a647301c8..514055261e3b 100644
--- a/libs/WindowManager/Shell/res/values-uz/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-uz/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Nomsiz)"</string>
<string name="pip_close" msgid="9135220303720555525">"Kadr ichida kadr – chiqish"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Butun ekran"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-vi/strings_tv.xml b/libs/WindowManager/Shell/res/values-vi/strings_tv.xml
index 082d12596076..e54d866f10e9 100644
--- a/libs/WindowManager/Shell/res/values-vi/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-vi/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Không có chương trình tiêu đề)"</string>
<string name="pip_close" msgid="9135220303720555525">"Đóng PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Toàn màn hình"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-zh-rCN/strings_tv.xml b/libs/WindowManager/Shell/res/values-zh-rCN/strings_tv.xml
index cb3fcf7c4c16..9ce1e6c41e75 100644
--- a/libs/WindowManager/Shell/res/values-zh-rCN/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rCN/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(节目没有标题)"</string>
<string name="pip_close" msgid="9135220303720555525">"关闭画中画"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"全屏"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-zh-rHK/strings_tv.xml b/libs/WindowManager/Shell/res/values-zh-rHK/strings_tv.xml
index 956243ed6e6d..984677290a85 100644
--- a/libs/WindowManager/Shell/res/values-zh-rHK/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rHK/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(沒有標題的節目)"</string>
<string name="pip_close" msgid="9135220303720555525">"關閉 PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"全螢幕"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-zh-rTW/strings_tv.xml b/libs/WindowManager/Shell/res/values-zh-rTW/strings_tv.xml
index 08b2f4bbca89..7314d486e8ff 100644
--- a/libs/WindowManager/Shell/res/values-zh-rTW/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rTW/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(無標題的節目)"</string>
<string name="pip_close" msgid="9135220303720555525">"關閉子母畫面"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"全螢幕"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values-zu/strings_tv.xml b/libs/WindowManager/Shell/res/values-zu/strings_tv.xml
index 89c7f498652d..63d9dd57fe54 100644
--- a/libs/WindowManager/Shell/res/values-zu/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-zu/strings_tv.xml
@@ -21,4 +21,6 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Alukho uhlelo lwesihloko)"</string>
<string name="pip_close" msgid="9135220303720555525">"Vala i-PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Iskrini esigcwele"</string>
+ <!-- no translation found for pip_move (1544227837964635439) -->
+ <skip />
</resources>
diff --git a/libs/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml
index 0cdaa206c156..1b8032b7077b 100644
--- a/libs/WindowManager/Shell/res/values/config.xml
+++ b/libs/WindowManager/Shell/res/values/config.xml
@@ -43,6 +43,9 @@
<!-- PiP minimum size, which is a % based off the shorter side of display width and height -->
<fraction name="config_pipShortestEdgePercent">40%</fraction>
+ <!-- Show PiP enter split icon, which allows apps to directly enter splitscreen from PiP. -->
+ <bool name="config_pipEnableEnterSplitButton">false</bool>
+
<!-- Animation duration when using long press on recents to dock -->
<integer name="long_press_dock_anim_duration">250</integer>
diff --git a/services/core/java/com/android/server/wm/BackGestureController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java
index f8f625486e56..b310dd638e6c 100644
--- a/services/core/java/com/android/server/wm/BackGestureController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java
@@ -14,24 +14,22 @@
* limitations under the License.
*/
-package com.android.server.wm;
+package com.android.wm.shell.back;
-import android.os.SystemProperties;
+import android.view.MotionEvent;
/**
- * Controller to handle actions related to the back gesture on the server side.
+ * Interface for SysUI to get access to the Back animation related methods.
*/
-public class BackGestureController {
+public interface BackAnimation {
- private static final String BACK_PREDICTABILITY_PROP = "persist.debug.back_predictability";
-
- public static boolean isEnabled() {
- return SystemProperties.getInt(BACK_PREDICTABILITY_PROP, 0) > 0;
- }
+ /**
+ * Called when a {@link MotionEvent} is generated by a back gesture.
+ */
+ void onBackMotion(MotionEvent event);
/**
- * Start a remote animation the back gesture.
+ * Sets whether the back gesture is past the trigger threshold or not.
*/
- public void startBackPreview() {
- }
+ void setTriggerBack(boolean triggerBack);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
new file mode 100644
index 000000000000..229e8ee04982
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -0,0 +1,286 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.back;
+
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityTaskManager;
+import android.app.IActivityTaskManager;
+import android.app.WindowConfiguration;
+import android.graphics.Point;
+import android.graphics.PointF;
+import android.hardware.HardwareBuffer;
+import android.os.RemoteException;
+import android.os.SystemProperties;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.SurfaceControl;
+import android.window.BackNavigationInfo;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.protolog.common.ProtoLog;
+import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.annotations.ShellMainThread;
+
+/**
+ * Controls the window animation run when a user initiates a back gesture.
+ */
+public class BackAnimationController {
+
+ private static final String BACK_PREDICTABILITY_PROP = "persist.debug.back_predictability";
+ public static final boolean IS_ENABLED = SystemProperties
+ .getInt(BACK_PREDICTABILITY_PROP, 0) > 0;
+ private static final String TAG = "BackAnimationController";
+
+ /**
+ * Location of the initial touch event of the back gesture.
+ */
+ private final PointF mInitTouchLocation = new PointF();
+
+ /**
+ * Raw delta between {@link #mInitTouchLocation} and the last touch location.
+ */
+ private final Point mTouchEventDelta = new Point();
+ private final ShellExecutor mShellExecutor;
+
+ /** True when a back gesture is ongoing */
+ private boolean mBackGestureStarted = false;
+
+ /** @see #setTriggerBack(boolean) */
+ private boolean mTriggerBack;
+
+ @Nullable
+ private BackNavigationInfo mBackNavigationInfo;
+ private final SurfaceControl.Transaction mTransaction;
+ private final IActivityTaskManager mActivityTaskManager;
+
+ public BackAnimationController(@ShellMainThread ShellExecutor shellExecutor) {
+ this(shellExecutor, new SurfaceControl.Transaction(), ActivityTaskManager.getService());
+ }
+
+ @VisibleForTesting
+ BackAnimationController(@NonNull ShellExecutor shellExecutor,
+ @NonNull SurfaceControl.Transaction transaction,
+ @NonNull IActivityTaskManager activityTaskManager) {
+ mShellExecutor = shellExecutor;
+ mTransaction = transaction;
+ mActivityTaskManager = activityTaskManager;
+ }
+
+ public BackAnimation getBackAnimationImpl() {
+ return mBackAnimation;
+ }
+
+ private final BackAnimation mBackAnimation = new BackAnimationImpl();
+
+ private class BackAnimationImpl implements BackAnimation {
+
+ @Override
+ public void onBackMotion(MotionEvent event) {
+ mShellExecutor.execute(() -> onMotionEvent(event));
+ }
+
+ @Override
+ public void setTriggerBack(boolean triggerBack) {
+ mShellExecutor.execute(() -> BackAnimationController.this.setTriggerBack(triggerBack));
+ }
+ }
+
+ /**
+ * Called when a new motion event needs to be transferred to this
+ * {@link BackAnimationController}
+ */
+ public void onMotionEvent(MotionEvent event) {
+ int action = event.getActionMasked();
+ if (action == MotionEvent.ACTION_DOWN) {
+ initAnimation(event);
+ } else if (action == MotionEvent.ACTION_MOVE) {
+ onMove(event);
+ } else if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
+ onGestureFinished();
+ }
+ }
+
+ private void initAnimation(MotionEvent event) {
+ ProtoLog.d(WM_SHELL_BACK_PREVIEW, "initAnimation mMotionStarted=%b", mBackGestureStarted);
+ if (mBackGestureStarted) {
+ Log.e(TAG, "Animation is being initialized but is already started.");
+ return;
+ }
+
+ if (mBackNavigationInfo != null) {
+ finishAnimation();
+ }
+ mInitTouchLocation.set(event.getX(), event.getY());
+ mBackGestureStarted = true;
+
+ try {
+ mBackNavigationInfo = mActivityTaskManager.startBackNavigation();
+ onBackNavigationInfoReceived(mBackNavigationInfo);
+ } catch (RemoteException remoteException) {
+ Log.e(TAG, "Failed to initAnimation", remoteException);
+ finishAnimation();
+ }
+ }
+
+ private void onBackNavigationInfoReceived(@Nullable BackNavigationInfo backNavigationInfo) {
+ if (backNavigationInfo == null
+ || backNavigationInfo.getDepartingWindowContainer() == null) {
+ Log.e(TAG, "Received BackNavigationInfo is null.");
+ finishAnimation();
+ return;
+ }
+
+ HardwareBuffer hardwareBuffer = backNavigationInfo.getScreenshotHardwareBuffer();
+ if (hardwareBuffer != null) {
+ displayTargetScreenshot(hardwareBuffer,
+ backNavigationInfo.getTaskWindowConfiguration());
+ }
+ mTransaction.apply();
+ }
+
+ /**
+ * Display the screenshot of the activity beneath.
+ *
+ * @param hardwareBuffer The buffer containing the screenshot.
+ */
+ private void displayTargetScreenshot(@NonNull HardwareBuffer hardwareBuffer,
+ WindowConfiguration taskWindowConfiguration) {
+ SurfaceControl screenshotSurface =
+ mBackNavigationInfo == null ? null : mBackNavigationInfo.getScreenshotSurface();
+ if (screenshotSurface == null) {
+ Log.e(TAG, "BackNavigationInfo doesn't contain a surface for the screenshot. ");
+ return;
+ }
+
+ // Scale the buffer to fill the whole Task
+ float sx = 1;
+ float sy = 1;
+ float w = taskWindowConfiguration.getBounds().width();
+ float h = taskWindowConfiguration.getBounds().height();
+
+ if (w != hardwareBuffer.getWidth()) {
+ sx = w / hardwareBuffer.getWidth();
+ }
+
+ if (h != hardwareBuffer.getHeight()) {
+ sy = h / hardwareBuffer.getHeight();
+ }
+ mTransaction.setScale(screenshotSurface, sx, sy);
+ mTransaction.setBuffer(screenshotSurface, hardwareBuffer);
+ mTransaction.setVisibility(screenshotSurface, true);
+ }
+
+ private void onMove(MotionEvent event) {
+ if (!mBackGestureStarted || mBackNavigationInfo == null) {
+ return;
+ }
+ int deltaX = Math.round(event.getX() - mInitTouchLocation.x);
+ int deltaY = Math.round(event.getY() - mInitTouchLocation.y);
+ ProtoLog.v(WM_SHELL_BACK_PREVIEW, "Runner move: %d %d", deltaX, deltaY);
+ SurfaceControl topWindowLeash = mBackNavigationInfo.getDepartingWindowContainer();
+ mTransaction.setPosition(topWindowLeash, deltaX, deltaY);
+ mTouchEventDelta.set(deltaX, deltaY);
+ mTransaction.apply();
+ }
+
+ private void onGestureFinished() {
+ ProtoLog.d(WM_SHELL_BACK_PREVIEW, "onGestureFinished() mTriggerBack == %s", mTriggerBack);
+ if (mBackGestureStarted) {
+ if (mTriggerBack) {
+ prepareTransition();
+ } else {
+ resetPositionAnimated();
+ }
+ }
+ mBackGestureStarted = false;
+ mTriggerBack = false;
+ }
+
+ /**
+ * Animate the top window leash to its initial position.
+ */
+ private void resetPositionAnimated() {
+ mBackGestureStarted = false;
+ // TODO(208786853) Handle overlap with a new coming gesture.
+ ProtoLog.d(WM_SHELL_BACK_PREVIEW, "Runner: Back not triggered, cancelling animation "
+ + "mLastPos=%s mInitTouch=%s", mTouchEventDelta, mInitTouchLocation);
+
+ // TODO(208427216) : Replace placeholder animation with an actual one.
+ ValueAnimator animation = ValueAnimator.ofFloat(0f, 1f).setDuration(200);
+ animation.addUpdateListener(animation1 -> {
+ if (mBackNavigationInfo == null) {
+ return;
+ }
+ float fraction = animation1.getAnimatedFraction();
+ int deltaX = Math.round(mTouchEventDelta.x - (mTouchEventDelta.x * fraction));
+ int deltaY = Math.round(mTouchEventDelta.y - (mTouchEventDelta.y * fraction));
+ mTransaction.setPosition(mBackNavigationInfo.getDepartingWindowContainer(),
+ deltaX, deltaY);
+ mTransaction.apply();
+ });
+
+ animation.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ ProtoLog.d(WM_SHELL_BACK_PREVIEW, "BackAnimationController: onAnimationEnd");
+ finishAnimation();
+ }
+ });
+ animation.start();
+ }
+
+ private void prepareTransition() {
+ ProtoLog.d(WM_SHELL_BACK_PREVIEW, "prepareTransition()");
+ mTriggerBack = false;
+ mBackGestureStarted = false;
+ }
+
+ /**
+ * Sets to true when the back gesture has passed the triggering threshold, false otherwise.
+ */
+ public void setTriggerBack(boolean triggerBack) {
+ mTriggerBack = triggerBack;
+ }
+
+ private void finishAnimation() {
+ ProtoLog.d(WM_SHELL_BACK_PREVIEW, "BackAnimationController: finishAnimation()");
+ mBackGestureStarted = false;
+ mTouchEventDelta.set(0, 0);
+ mInitTouchLocation.set(0, 0);
+ BackNavigationInfo backNavigationInfo = mBackNavigationInfo;
+ mBackNavigationInfo = null;
+ if (backNavigationInfo == null) {
+ return;
+ }
+ SurfaceControl topWindowLeash = backNavigationInfo.getDepartingWindowContainer();
+ if (topWindowLeash != null && topWindowLeash.isValid()) {
+ mTransaction.remove(topWindowLeash);
+ }
+ SurfaceControl screenshotSurface = backNavigationInfo.getScreenshotSurface();
+ if (screenshotSurface != null && screenshotSurface.isValid()) {
+ mTransaction.remove(screenshotSurface);
+ }
+ mTransaction.apply();
+ backNavigationInfo.onBackNavigationFinished();
+ }
+}
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 682363904a23..57cb7a5a57d7 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
@@ -17,6 +17,8 @@
package com.android.wm.shell.bubbles;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_DELETED;
+import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED;
import static android.service.notification.NotificationListenerService.REASON_CANCEL;
import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE;
@@ -42,6 +44,7 @@ import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.Notification;
+import android.app.NotificationChannel;
import android.app.PendingIntent;
import android.content.Context;
import android.content.pm.ActivityInfo;
@@ -1092,6 +1095,24 @@ public class BubbleController {
}
}
+ @VisibleForTesting
+ public void onNotificationChannelModified(String pkg, UserHandle user,
+ NotificationChannel channel, int modificationType) {
+ // Only query overflow bubbles here because active bubbles will have an active notification
+ // and channel changes we care about would result in a ranking update.
+ List<Bubble> overflowBubbles = new ArrayList<>(mBubbleData.getOverflowBubbles());
+ for (int i = 0; i < overflowBubbles.size(); i++) {
+ Bubble b = overflowBubbles.get(i);
+ if (Objects.equals(b.getShortcutId(), channel.getConversationId())
+ && b.getPackageName().equals(pkg)
+ && b.getUser().getIdentifier() == user.getIdentifier()) {
+ if (!channel.canBubble() || channel.isDeleted()) {
+ mBubbleData.dismissBubbleWithKey(b.getKey(), DISMISS_NO_LONGER_BUBBLE);
+ }
+ }
+ }
+ }
+
/**
* Retrieves any bubbles that are part of the notification group represented by the provided
* group key.
@@ -1670,6 +1691,19 @@ public class BubbleController {
}
@Override
+ public void onNotificationChannelModified(String pkg,
+ UserHandle user, NotificationChannel channel, int modificationType) {
+ // Bubbles only cares about updates or deletions.
+ if (modificationType == NOTIFICATION_CHANNEL_OR_GROUP_UPDATED
+ || modificationType == NOTIFICATION_CHANNEL_OR_GROUP_DELETED) {
+ mMainExecutor.execute(() -> {
+ BubbleController.this.onNotificationChannelModified(pkg, user, channel,
+ modificationType);
+ });
+ }
+ }
+
+ @Override
public void onStatusBarVisibilityChanged(boolean visible) {
mMainExecutor.execute(() -> {
BubbleController.this.onStatusBarVisibilityChanged(visible);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
index c82249b8a369..af403d23d69c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
@@ -21,9 +21,12 @@ import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.SOURCE;
+import android.app.NotificationChannel;
import android.content.pm.UserInfo;
import android.content.res.Configuration;
import android.os.Bundle;
+import android.os.UserHandle;
+import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationListenerService.RankingMap;
import android.util.ArraySet;
import android.util.Pair;
@@ -191,10 +194,26 @@ public interface Bubbles {
* @param entryDataByKey a map of ranking key to bubble entry and whether the entry should
* bubble up
*/
- void onRankingUpdated(RankingMap rankingMap,
+ void onRankingUpdated(
+ RankingMap rankingMap,
HashMap<String, Pair<BubbleEntry, Boolean>> entryDataByKey);
/**
+ * Called when a notification channel is modified, in response to
+ * {@link NotificationListenerService#onNotificationChannelModified}.
+ *
+ * @param pkg the package the notification channel belongs to.
+ * @param user the user the notification channel belongs to.
+ * @param channel the channel being modified.
+ * @param modificationType the type of modification that occurred to the channel.
+ */
+ void onNotificationChannelModified(
+ String pkg,
+ UserHandle user,
+ NotificationChannel channel,
+ int modificationType);
+
+ /**
* Called when the status bar has become visible or invisible (either permanently or
* temporarily).
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
index 7db49f05a5dc..e2bc36028405 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
@@ -19,6 +19,7 @@ package com.android.wm.shell.common;
import android.annotation.Nullable;
import android.content.Context;
import android.content.res.Configuration;
+import android.graphics.Rect;
import android.hardware.display.DisplayManager;
import android.os.RemoteException;
import android.util.Slog;
@@ -34,6 +35,7 @@ import com.android.wm.shell.common.DisplayChangeController.OnDisplayChangingList
import com.android.wm.shell.common.annotations.ShellMainThread;
import java.util.ArrayList;
+import java.util.List;
/**
* This module deals with display rotations coming from WM. When WM starts a rotation: after it has
@@ -243,6 +245,19 @@ public class DisplayController {
}
}
+ private void onKeepClearAreasChanged(int displayId, List<Rect> keepClearAreas) {
+ synchronized (mDisplays) {
+ if (mDisplays.get(displayId) == null || getDisplay(displayId) == null) {
+ Slog.w(TAG, "Skipping onKeepClearAreasChanged on unknown"
+ + " display, displayId=" + displayId);
+ return;
+ }
+ for (int i = mDisplayChangedListeners.size() - 1; i >= 0; --i) {
+ mDisplayChangedListeners.get(i).onKeepClearAreasChanged(displayId, keepClearAreas);
+ }
+ }
+ }
+
private static class DisplayRecord {
private int mDisplayId;
private Context mContext;
@@ -301,6 +316,13 @@ public class DisplayController {
DisplayController.this.onFixedRotationFinished(displayId);
});
}
+
+ @Override
+ public void onKeepClearAreasChanged(int displayId, List<Rect> keepClearAreas) {
+ mMainExecutor.execute(() -> {
+ DisplayController.this.onKeepClearAreasChanged(displayId, keepClearAreas);
+ });
+ }
}
/**
@@ -335,5 +357,10 @@ public class DisplayController {
* Called when fixed rotation on a display is finished.
*/
default void onFixedRotationFinished(int displayId) {}
+
+ /**
+ * Called when keep-clear areas on a display have changed.
+ */
+ default void onKeepClearAreasChanged(int displayId, List<Rect> keepClearAreas) {}
}
}
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 9e012598554b..aac1d0626d30 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
@@ -17,13 +17,10 @@ package com.android.wm.shell.common.magnetictarget
import android.annotation.SuppressLint
import android.content.Context
-import android.database.ContentObserver
import android.graphics.PointF
-import android.os.Handler
-import android.os.UserHandle
+import android.os.VibrationAttributes
import android.os.VibrationEffect
import android.os.Vibrator
-import android.provider.Settings
import android.view.MotionEvent
import android.view.VelocityTracker
import android.view.View
@@ -147,6 +144,8 @@ abstract class MagnetizedObject<T : Any>(
private val velocityTracker: VelocityTracker = VelocityTracker.obtain()
private val vibrator: Vibrator = context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
+ private val vibrationAttributes: VibrationAttributes = VibrationAttributes.createForUsage(
+ VibrationAttributes.USAGE_TOUCH)
private var touchDown = PointF()
private var touchSlop = 0
@@ -268,10 +267,6 @@ abstract class MagnetizedObject<T : Any>(
*/
var flungIntoTargetSpringConfig = springConfig
- init {
- initHapticSettingObserver(context)
- }
-
/**
* Adds the provided MagneticTarget to this object. The object will now be attracted to the
* target if it strays within its magnetic field or is flung towards it.
@@ -468,8 +463,8 @@ abstract class MagnetizedObject<T : Any>(
/** Plays the given vibration effect if haptics are enabled. */
@SuppressLint("MissingPermission")
private fun vibrateIfEnabled(effectId: Int) {
- if (hapticsEnabled && systemHapticsEnabled) {
- vibrator.vibrate(VibrationEffect.createPredefined(effectId))
+ if (hapticsEnabled) {
+ vibrator.vibrate(VibrationEffect.createPredefined(effectId), vibrationAttributes)
}
}
@@ -622,44 +617,6 @@ abstract class MagnetizedObject<T : Any>(
}
companion object {
-
- /**
- * Whether the HAPTIC_FEEDBACK_ENABLED setting is true.
- *
- * We put it in the companion object because we need to register a settings observer and
- * [MagnetizedObject] doesn't have an obvious lifecycle so we don't have a good time to
- * remove that observer. Since this settings is shared among all instances we just let all
- * instances read from this value.
- */
- private var systemHapticsEnabled = false
- private var hapticSettingObserverInitialized = false
-
- private fun initHapticSettingObserver(context: Context) {
- if (hapticSettingObserverInitialized) {
- return
- }
-
- val hapticSettingObserver =
- object : ContentObserver(Handler.getMain()) {
- override fun onChange(selfChange: Boolean) {
- systemHapticsEnabled =
- Settings.System.getIntForUser(
- context.contentResolver,
- Settings.System.HAPTIC_FEEDBACK_ENABLED,
- 0,
- UserHandle.USER_CURRENT) != 0
- }
- }
-
- context.contentResolver.registerContentObserver(
- Settings.System.getUriFor(Settings.System.HAPTIC_FEEDBACK_ENABLED),
- true /* notifyForDescendants */, hapticSettingObserver)
-
- // Trigger the observer once to initialize systemHapticsEnabled.
- hapticSettingObserver.onChange(false /* selfChange */)
- hapticSettingObserverInitialized = true
- }
-
/**
* Magnetizes the given view. Magnetized views are attracted to one or more magnetic
* targets. Magnetic targets attract objects that are dragged near them, and hold them there
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index 23d9b8b14159..f61e62444366 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -40,6 +40,8 @@ import com.android.wm.shell.TaskViewTransitions;
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.apppairs.AppPairs;
import com.android.wm.shell.apppairs.AppPairsController;
+import com.android.wm.shell.back.BackAnimation;
+import com.android.wm.shell.back.BackAnimationController;
import com.android.wm.shell.bubbles.BubbleController;
import com.android.wm.shell.bubbles.Bubbles;
import com.android.wm.shell.common.DisplayController;
@@ -238,6 +240,17 @@ public abstract class WMShellBaseModule {
}
//
+ // Back animation
+ //
+
+ @WMSingleton
+ @Provides
+ static Optional<BackAnimation> provideBackAnimation(
+ Optional<BackAnimationController> backAnimationController) {
+ return backAnimationController.map(BackAnimationController::getBackAnimationImpl);
+ }
+
+ //
// Bubbles (optional feature)
//
@@ -678,4 +691,16 @@ public abstract class WMShellBaseModule {
legacySplitScreenOptional, splitScreenOptional, pipOptional, oneHandedOptional,
hideDisplayCutout, appPairsOptional, recentTasksOptional, mainExecutor);
}
+
+ @WMSingleton
+ @Provides
+ static Optional<BackAnimationController> provideBackAnimationController(
+ @ShellMainThread ShellExecutor shellExecutor
+ ) {
+ if (BackAnimationController.IS_ENABLED) {
+ return Optional.of(
+ new BackAnimationController(shellExecutor));
+ }
+ return Optional.empty();
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index cc37caec5225..7879e7a5bb00 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -246,10 +246,11 @@ public class WMShellModule {
PipBoundsState pipBoundsState, PipMediaController pipMediaController,
SystemWindows systemWindows,
Optional<SplitScreenController> splitScreenOptional,
+ PipUiEventLogger pipUiEventLogger,
@ShellMainThread ShellExecutor mainExecutor,
@ShellMainThread Handler mainHandler) {
return new PhonePipMenuController(context, pipBoundsState, pipMediaController,
- systemWindows, splitScreenOptional, mainExecutor, mainHandler);
+ systemWindows, splitScreenOptional, pipUiEventLogger, mainExecutor, mainHandler);
}
@WMSingleton
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 4c09a4e9938f..17005ea7d500 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
@@ -397,8 +397,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
return;
}
- mPipUiEventLoggerLogger.log(
- PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_EXPAND_TO_FULLSCREEN);
final WindowContainerTransaction wct = new WindowContainerTransaction();
if (ENABLE_SHELL_TRANSITIONS) {
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 69e78364c5e1..3e5d5f645725 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
@@ -330,12 +330,10 @@ public class PipTransition extends PipTransitionController {
@NonNull TransitionInfo.Change pipChange) {
final int rotateDelta = deltaRotation(displayRotationChange.getStartRotation(),
displayRotationChange.getEndRotation());
- final int displayW = displayRotationChange.getEndAbsBounds().width();
- final int displayH = displayRotationChange.getEndAbsBounds().height();
// Counter-rotate all "going-away" things since they are still in the old orientation.
final CounterRotatorHelper rotator = new CounterRotatorHelper();
- rotator.handleClosingChanges(info, startTransaction, rotateDelta, displayW, displayH);
+ rotator.handleClosingChanges(info, startTransaction, displayRotationChange);
mFinishCallback = (wct, wctCB) -> {
mPipOrganizer.onExitPipFinished(pipChange.getTaskInfo());
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUiEventLogger.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUiEventLogger.java
index a0a76d801cf4..9c23a32a7d2b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUiEventLogger.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUiEventLogger.java
@@ -107,7 +107,10 @@ public class PipUiEventLogger {
PICTURE_IN_PICTURE_STASH_LEFT(710),
@UiEvent(doc = "User stashed picture-in-picture to the right side")
- PICTURE_IN_PICTURE_STASH_RIGHT(711);
+ PICTURE_IN_PICTURE_STASH_RIGHT(711),
+
+ @UiEvent(doc = "User taps on the settings button in PiP menu")
+ PICTURE_IN_PICTURE_SHOW_SETTINGS(933);
private final int mId;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
index 101a55d8d367..6ec8f5b924f8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
@@ -44,6 +44,7 @@ import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipMediaController;
import com.android.wm.shell.pip.PipMediaController.ActionListener;
import com.android.wm.shell.pip.PipMenuController;
+import com.android.wm.shell.pip.PipUiEventLogger;
import com.android.wm.shell.splitscreen.SplitScreenController;
import java.io.PrintWriter;
@@ -118,6 +119,7 @@ public class PhonePipMenuController implements PipMenuController {
private final ArrayList<Listener> mListeners = new ArrayList<>();
private final SystemWindows mSystemWindows;
private final Optional<SplitScreenController> mSplitScreenController;
+ private final PipUiEventLogger mPipUiEventLogger;
private ParceledListSlice<RemoteAction> mAppActions;
private ParceledListSlice<RemoteAction> mMediaActions;
private SyncRtSurfaceTransactionApplier mApplier;
@@ -150,6 +152,7 @@ public class PhonePipMenuController implements PipMenuController {
public PhonePipMenuController(Context context, PipBoundsState pipBoundsState,
PipMediaController mediaController, SystemWindows systemWindows,
Optional<SplitScreenController> splitScreenOptional,
+ PipUiEventLogger pipUiEventLogger,
ShellExecutor mainExecutor, Handler mainHandler) {
mContext = context;
mPipBoundsState = pipBoundsState;
@@ -158,6 +161,7 @@ public class PhonePipMenuController implements PipMenuController {
mMainExecutor = mainExecutor;
mMainHandler = mainHandler;
mSplitScreenController = splitScreenOptional;
+ mPipUiEventLogger = pipUiEventLogger;
}
public boolean isMenuVisible() {
@@ -187,7 +191,7 @@ public class PhonePipMenuController implements PipMenuController {
detachPipMenuView();
}
mPipMenuView = new PipMenuView(mContext, this, mMainExecutor, mMainHandler,
- mSplitScreenController);
+ mSplitScreenController, mPipUiEventLogger);
mSystemWindows.addView(mPipMenuView,
getPipMenuLayoutParams(MENU_WINDOW_TITLE, 0 /* width */, 0 /* height */),
0, SHELL_ROOT_LAYER_PIP);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
index da4bbe81a3e6..225305bd5178 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
@@ -63,6 +63,7 @@ import android.widget.LinearLayout;
import com.android.wm.shell.R;
import com.android.wm.shell.animation.Interpolators;
import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.pip.PipUiEventLogger;
import com.android.wm.shell.pip.PipUtils;
import com.android.wm.shell.splitscreen.SplitScreenController;
@@ -104,8 +105,6 @@ public class PipMenuView extends FrameLayout {
private static final float MENU_BACKGROUND_ALPHA = 0.3f;
private static final float DISABLED_ACTION_ALPHA = 0.54f;
- private static final boolean ENABLE_ENTER_SPLIT = true;
-
private int mMenuState;
private boolean mAllowMenuTimeout = true;
private boolean mAllowTouches = true;
@@ -121,8 +120,9 @@ public class PipMenuView extends FrameLayout {
private int mBetweenActionPaddingLand;
private AnimatorSet mMenuContainerAnimator;
- private PhonePipMenuController mController;
- private Optional<SplitScreenController> mSplitScreenControllerOptional;
+ private final PhonePipMenuController mController;
+ private final Optional<SplitScreenController> mSplitScreenControllerOptional;
+ private final PipUiEventLogger mPipUiEventLogger;
private ValueAnimator.AnimatorUpdateListener mMenuBgUpdateListener =
new ValueAnimator.AnimatorUpdateListener() {
@@ -152,13 +152,15 @@ public class PipMenuView extends FrameLayout {
public PipMenuView(Context context, PhonePipMenuController controller,
ShellExecutor mainExecutor, Handler mainHandler,
- Optional<SplitScreenController> splitScreenController) {
+ Optional<SplitScreenController> splitScreenController,
+ PipUiEventLogger pipUiEventLogger) {
super(context, null, 0);
mContext = context;
mController = controller;
mMainExecutor = mainExecutor;
mMainHandler = mainHandler;
mSplitScreenControllerOptional = splitScreenController;
+ mPipUiEventLogger = pipUiEventLogger;
mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
inflate(context, R.layout.pip_menu, this);
@@ -277,6 +279,8 @@ public class PipMenuView extends FrameLayout {
boolean resizeMenuOnShow, boolean withDelay, boolean showResizeHandle) {
mAllowMenuTimeout = allowMenuTimeout;
mDidLastShowMenuResize = resizeMenuOnShow;
+ final boolean enableEnterSplit =
+ mContext.getResources().getBoolean(R.bool.config_pipEnableEnterSplitButton);
if (mMenuState != menuState) {
// Disallow touches if the menu needs to resize while showing, and we are transitioning
// to/from a full menu state.
@@ -297,7 +301,7 @@ public class PipMenuView extends FrameLayout {
mDismissButton.getAlpha(), 1f);
ObjectAnimator enterSplitAnim = ObjectAnimator.ofFloat(mEnterSplitButton, View.ALPHA,
mEnterSplitButton.getAlpha(),
- ENABLE_ENTER_SPLIT && mFocusedTaskAllowSplitScreen ? 1f : 0f);
+ enableEnterSplit && mFocusedTaskAllowSplitScreen ? 1f : 0f);
if (menuState == MENU_STATE_FULL) {
mMenuContainerAnimator.playTogether(menuAnim, settingsAnim, dismissAnim,
enterSplitAnim);
@@ -539,6 +543,8 @@ public class PipMenuView extends FrameLayout {
// handles the message
hideMenu(mController::onPipExpand, false /* notifyMenuVisibility */, true /* resize */,
ANIM_TYPE_HIDE);
+ mPipUiEventLogger.log(
+ PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_EXPAND_TO_FULLSCREEN);
}
private void dismissPip() {
@@ -547,6 +553,7 @@ public class PipMenuView extends FrameLayout {
// any other dismissal that will update the touch state and fade out the PIP task
// and the menu view at the same time.
mController.onPipDismiss();
+ mPipUiEventLogger.log(PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_TAP_TO_REMOVE);
}
}
@@ -566,6 +573,7 @@ public class PipMenuView extends FrameLayout {
Uri.fromParts("package", topPipActivityInfo.first.getPackageName(), null));
settingsIntent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK);
mContext.startActivityAsUser(settingsIntent, UserHandle.of(topPipActivityInfo.second));
+ mPipUiEventLogger.log(PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_SHOW_SETTINGS);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
index 3ace5f405d36..350f2856e2bc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
@@ -149,7 +149,6 @@ public class PipTouchHandler {
@Override
public void onPipDismiss() {
- mPipUiEventLogger.log(PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_TAP_TO_REMOVE);
mTouchState.removeDoubleTapTimeoutCallback();
mMotionHelper.dismissPip();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
index 79c1df2174b9..20c4e21a811d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
@@ -34,6 +34,8 @@ public enum ShellProtoLogGroup implements IProtoLogGroup {
Consts.TAG_WM_SHELL),
WM_SHELL_STARTING_WINDOW(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
Consts.TAG_WM_STARTING_WINDOW),
+ WM_SHELL_BACK_PREVIEW(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true,
+ "ShellBackPreview"),
TEST_GROUP(true, true, false, "WindowManagerShellProtoLogTest");
private final boolean mEnabled;
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 8664d9be3340..d30d0cc95f46 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
@@ -70,7 +70,8 @@ class SplitScreenTransitions {
IBinder mPendingRecent = null;
private IBinder mAnimatingTransition = null;
- private OneShotRemoteHandler mRemoteHandler = null;
+ private OneShotRemoteHandler mPendingRemoteHandler = null;
+ private OneShotRemoteHandler mActiveRemoteHandler = null;
private final Transitions.TransitionFinishCallback mRemoteFinishCB = this::onFinish;
@@ -96,10 +97,11 @@ class SplitScreenTransitions {
@NonNull WindowContainerToken mainRoot, @NonNull WindowContainerToken sideRoot) {
mFinishCallback = finishCallback;
mAnimatingTransition = transition;
- if (mRemoteHandler != null) {
- mRemoteHandler.startAnimation(transition, info, startTransaction, finishTransaction,
- mRemoteFinishCB);
- mRemoteHandler = null;
+ if (mPendingRemoteHandler != null) {
+ mPendingRemoteHandler.startAnimation(transition, info, startTransaction,
+ finishTransaction, mRemoteFinishCB);
+ mActiveRemoteHandler = mPendingRemoteHandler;
+ mPendingRemoteHandler = null;
return;
}
playInternalAnimation(transition, info, startTransaction, mainRoot, sideRoot);
@@ -172,15 +174,14 @@ class SplitScreenTransitions {
IBinder startEnterTransition(@WindowManager.TransitionType int transitType,
@NonNull WindowContainerTransaction wct, @Nullable RemoteTransition remoteTransition,
@NonNull Transitions.TransitionHandler handler) {
+ final IBinder transition = mTransitions.startTransition(transitType, wct, handler);
+ mPendingEnter = transition;
+
if (remoteTransition != null) {
// Wrapping it for ease-of-use (OneShot handles all the binder linking/death stuff)
- mRemoteHandler = new OneShotRemoteHandler(
+ mPendingRemoteHandler = new OneShotRemoteHandler(
mTransitions.getMainExecutor(), remoteTransition);
- }
- final IBinder transition = mTransitions.startTransition(transitType, wct, handler);
- mPendingEnter = transition;
- if (mRemoteHandler != null) {
- mRemoteHandler.setTransition(transition);
+ mPendingRemoteHandler.setTransition(transition);
}
return transition;
}
@@ -211,9 +212,9 @@ class SplitScreenTransitions {
if (remoteTransition != null) {
// Wrapping it for ease-of-use (OneShot handles all the binder linking/death stuff)
- mRemoteHandler = new OneShotRemoteHandler(
+ mPendingRemoteHandler = new OneShotRemoteHandler(
mTransitions.getMainExecutor(), remoteTransition);
- mRemoteHandler.setTransition(transition);
+ mPendingRemoteHandler.setTransition(transition);
}
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " splitTransition "
@@ -221,6 +222,13 @@ class SplitScreenTransitions {
return transition;
}
+ void mergeAnimation(IBinder transition, TransitionInfo info, SurfaceControl.Transaction t,
+ IBinder mergeTarget, Transitions.TransitionFinishCallback finishCallback) {
+ if (mergeTarget == mAnimatingTransition && mActiveRemoteHandler != null) {
+ mActiveRemoteHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback);
+ }
+ }
+
void onFinish(WindowContainerTransaction wct, WindowContainerTransactionCallback wctCB) {
if (!mAnimations.isEmpty()) return;
mOnFinish.run();
@@ -241,11 +249,13 @@ class SplitScreenTransitions {
}
if (mAnimatingTransition == mPendingRecent) {
// If the wct is not null while finishing recent transition, it indicates it's not
- // returning to home and hence needing the wct to reorder tasks.
- final boolean toHome = wct == null;
- mStageCoordinator.finishRecentAnimation(toHome);
+ // dismissing split and thus need to reorder split task so they can be on top again.
+ final boolean dismissSplit = wct == null;
+ mStageCoordinator.finishRecentAnimation(dismissSplit);
mPendingRecent = null;
}
+ mPendingRemoteHandler = null;
+ mActiveRemoteHandler = null;
mAnimatingTransition = null;
}
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 c8120509f0db..e592101d2b20 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
@@ -166,17 +166,6 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
@StageType
private int mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED;
- private final Runnable mOnTransitionAnimationComplete = () -> {
- // If still playing, let it finish.
- if (!isSplitScreenVisible()) {
- // Update divider state after animation so that it is still around and positioned
- // properly for the animation itself.
- mSplitLayout.release();
- mSplitLayout.resetDividerPosition();
- mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED;
- }
- };
-
private final SplitWindowManager.ParentContainerCallbacks mParentContainerCallbacks =
new SplitWindowManager.ParentContainerCallbacks() {
@Override
@@ -237,7 +226,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
deviceStateManager.registerCallback(taskOrganizer.getExecutor(),
new DeviceStateManager.FoldStateListener(mContext, this::onFoldedStateChanged));
mSplitTransitions = new SplitScreenTransitions(transactionPool, transitions,
- mOnTransitionAnimationComplete, this);
+ this::onTransitionAnimationComplete, this);
mDisplayController.addDisplayWindowListener(this);
mDisplayLayout = new DisplayLayout(displayController.getDisplayLayout(displayId));
transitions.addHandler(this);
@@ -267,7 +256,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mRootTDAOrganizer.registerListener(displayId, this);
mSplitLayout = splitLayout;
mSplitTransitions = new SplitScreenTransitions(transactionPool, transitions,
- mOnTransitionAnimationComplete, this);
+ this::onTransitionAnimationComplete, this);
mMainUnfoldController = unfoldControllerProvider.get().orElse(null);
mSideUnfoldController = unfoldControllerProvider.get().orElse(null);
mLogger = logger;
@@ -1234,7 +1223,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
final ActivityManager.RunningTaskInfo triggerTask = request.getTriggerTask();
if (triggerTask == null) {
// Still want to monitor everything while in split-screen, so return non-null.
- return isSplitScreenVisible() ? new WindowContainerTransaction() : null;
+ return mMainStage.isActive() ? new WindowContainerTransaction() : null;
} else if (triggerTask.displayId != mDisplayId) {
// Skip handling task on the other display.
return null;
@@ -1250,7 +1239,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mRecentTasks.ifPresent(recentTasks -> recentTasks.removeSplitPair(triggerTask.taskId));
}
- if (isSplitScreenVisible()) {
+ if (mMainStage.isActive()) {
// Try to handle everything while in split-screen, so return a WCT even if it's empty.
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " split is active so using split"
+ "Transition to handle request. triggerTask=%d type=%s mainChildren=%d"
@@ -1296,6 +1285,13 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
@Override
+ public void mergeAnimation(IBinder transition, TransitionInfo info,
+ SurfaceControl.Transaction t, IBinder mergeTarget,
+ Transitions.TransitionFinishCallback finishCallback) {
+ mSplitTransitions.mergeAnimation(transition, info, t, mergeTarget, finishCallback);
+ }
+
+ @Override
public void onTransitionMerged(@NonNull IBinder transition) {
// Once the pending enter transition got merged, make sure to bring divider bar visible and
// clear the pending transition from cache to prevent mess-up the following state.
@@ -1375,6 +1371,17 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
return true;
}
+ void onTransitionAnimationComplete() {
+ // If still playing, let it finish.
+ if (!mMainStage.isActive()) {
+ // Update divider state after animation so that it is still around and positioned
+ // properly for the animation itself.
+ mSplitLayout.release();
+ mSplitLayout.resetDividerPosition();
+ mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED;
+ }
+ }
+
private boolean startPendingEnterAnimation(@NonNull IBinder transition,
@NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t) {
// First, verify that we actually have opened apps in both splits.
@@ -1513,8 +1520,17 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
return true;
}
- void finishRecentAnimation(boolean toHome) {
- if (toHome) {
+ void finishRecentAnimation(boolean dismissSplit) {
+ // Exclude the case that the split screen has been dismissed already.
+ if (!mMainStage.isActive()) {
+ // The latest split dismissing transition might be a no-op transition and thus won't
+ // callback startAnimation, update split visibility here to cover this kind of no-op
+ // transition case.
+ setSplitsVisible(false);
+ return;
+ }
+
+ if (dismissSplit) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, wct);
mSplitTransitions.startDismissTransition(null /* transition */, wct, this,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/CounterRotatorHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/CounterRotatorHelper.java
index 8c71f749b2c3..19133e29de4b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/CounterRotatorHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/CounterRotatorHelper.java
@@ -18,7 +18,9 @@ package com.android.wm.shell.transition;
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
+import android.graphics.Rect;
import android.util.ArrayMap;
+import android.util.RotationUtils;
import android.view.SurfaceControl;
import android.window.TransitionInfo;
import android.window.WindowContainerToken;
@@ -35,11 +37,21 @@ import java.util.List;
*/
public class CounterRotatorHelper {
private final ArrayMap<WindowContainerToken, CounterRotator> mRotatorMap = new ArrayMap<>();
+ private final Rect mLastDisplayBounds = new Rect();
+ private int mLastRotationDelta;
/** Puts the surface controls of closing changes to counter-rotated surfaces. */
public void handleClosingChanges(@NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction startTransaction,
- int rotateDelta, int displayW, int displayH) {
+ @NonNull TransitionInfo.Change displayRotationChange) {
+ final int rotationDelta = RotationUtils.deltaRotation(
+ displayRotationChange.getStartRotation(), displayRotationChange.getEndRotation());
+ final Rect displayBounds = displayRotationChange.getEndAbsBounds();
+ final int displayW = displayBounds.width();
+ final int displayH = displayBounds.height();
+ mLastRotationDelta = rotationDelta;
+ mLastDisplayBounds.set(displayBounds);
+
final List<TransitionInfo.Change> changes = info.getChanges();
final int numChanges = changes.size();
for (int i = numChanges - 1; i >= 0; --i) {
@@ -53,7 +65,7 @@ public class CounterRotatorHelper {
CounterRotator crot = mRotatorMap.get(parent);
if (crot == null) {
crot = new CounterRotator();
- crot.setup(startTransaction, info.getChange(parent).getLeash(), rotateDelta,
+ crot.setup(startTransaction, info.getChange(parent).getLeash(), rotationDelta,
displayW, displayH);
final SurfaceControl rotatorSc = crot.getSurface();
if (rotatorSc != null) {
@@ -70,6 +82,18 @@ public class CounterRotatorHelper {
}
/**
+ * Returns the rotated end bounds if the change is put in previous rotation. Otherwise the
+ * original end bounds are returned.
+ */
+ @NonNull
+ public Rect getEndBoundsInStartRotation(@NonNull TransitionInfo.Change change) {
+ if (mLastRotationDelta == 0) return change.getEndAbsBounds();
+ final Rect rotatedBounds = new Rect(change.getEndAbsBounds());
+ RotationUtils.rotateBounds(rotatedBounds, mLastDisplayBounds, mLastRotationDelta);
+ return rotatedBounds;
+ }
+
+ /**
* Removes the counter rotation surface in the finish transaction. No need to reparent the
* children as the finish transaction should have already taken care of that.
*
@@ -80,5 +104,6 @@ public class CounterRotatorHelper {
mRotatorMap.valueAt(i).cleanUp(finishTransaction);
}
mRotatorMap.clear();
+ mLastRotationDelta = 0;
}
}
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 5054e60d09f8..13e81bdb3c0b 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
@@ -124,6 +124,7 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
/** Keeps track of the currently-running animations associated with each transition. */
private final ArrayMap<IBinder, ArrayList<Animator>> mAnimations = new ArrayMap<>();
+ private final CounterRotatorHelper mRotator = new CounterRotatorHelper();
private final Rect mInsets = new Rect(0, 0, 0, 0);
private float mTransitionAnimationScaleSetting = 1.0f;
@@ -277,8 +278,6 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
final ArrayList<Animator> animations = new ArrayList<>();
mAnimations.put(transition, animations);
- final CounterRotatorHelper rotator = new CounterRotatorHelper();
-
final Runnable onAnimFinish = () -> {
if (!animations.isEmpty()) return;
@@ -298,9 +297,6 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
final boolean isTask = change.getTaskInfo() != null;
if (change.getMode() == TRANSIT_CHANGE && (change.getFlags() & FLAG_IS_DISPLAY) != 0) {
- int rotateDelta = change.getEndRotation() - change.getStartRotation();
- int displayW = change.getEndAbsBounds().width();
- int displayH = change.getEndAbsBounds().height();
if (info.getType() == TRANSIT_CHANGE) {
boolean isSeamless = isRotationSeamless(info, mDisplayController);
final int anim = getRotationAnimation(info);
@@ -314,8 +310,7 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
}
} else {
// Opening/closing an app into a new orientation.
- rotator.handleClosingChanges(info, startTransaction, rotateDelta,
- displayW, displayH);
+ mRotator.handleClosingChanges(info, startTransaction, change);
}
}
@@ -384,9 +379,12 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
}
}
+ final Rect clipRect = Transitions.isClosingType(change.getMode())
+ ? mRotator.getEndBoundsInStartRotation(change)
+ : change.getEndAbsBounds();
startSurfaceAnimation(animations, a, change.getLeash(), onAnimFinish,
mTransactionPool, mMainExecutor, mAnimExecutor, null /* position */,
- cornerRadius, change.getEndAbsBounds());
+ cornerRadius, clipRect);
if (info.getAnimationOptions() != null) {
attachThumbnail(animations, onAnimFinish, change, info.getAnimationOptions(),
@@ -401,7 +399,7 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
}
startTransaction.apply();
- rotator.cleanUp(finishTransaction);
+ mRotator.cleanUp(finishTransaction);
TransitionMetrics.getInstance().reportAnimationStart(transition);
// run finish now in-case there are no animations
onAnimFinish.run();
@@ -458,6 +456,9 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
final TransitionInfo.AnimationOptions options = info.getAnimationOptions();
final int overrideType = options != null ? options.getType() : ANIM_NONE;
final boolean canCustomContainer = isTask ? !sDisableCustomTaskAnimationProperty : true;
+ final Rect endBounds = Transitions.isClosingType(changeMode)
+ ? mRotator.getEndBoundsInStartRotation(change)
+ : change.getEndAbsBounds();
if (info.isKeyguardGoingAway()) {
a = mTransitionAnimation.loadKeyguardExitAnimation(flags,
@@ -475,8 +476,7 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
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());
+ a = mTransitionAnimation.createRelaunchAnimation(endBounds, mInsets, endBounds);
} else if (overrideType == ANIM_CUSTOM
&& (canCustomContainer || options.getOverrideTaskTransition())) {
a = mTransitionAnimation.loadAnimationRes(options.getPackageName(), enter
@@ -485,16 +485,15 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
a = mTransitionAnimation.loadCrossProfileAppEnterAnimation();
} else if (overrideType == ANIM_CLIP_REVEAL) {
a = mTransitionAnimation.createClipRevealAnimationLocked(type, wallpaperTransit, enter,
- change.getEndAbsBounds(), change.getEndAbsBounds(),
- options.getTransitionBounds());
+ endBounds, endBounds, options.getTransitionBounds());
} else if (overrideType == ANIM_SCALE_UP) {
a = mTransitionAnimation.createScaleUpAnimationLocked(type, wallpaperTransit, enter,
- change.getEndAbsBounds(), options.getTransitionBounds());
+ endBounds, 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(),
+ endBounds, 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
@@ -567,8 +566,9 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
if (a != null) {
if (!a.isInitialized()) {
- Rect end = change.getEndAbsBounds();
- a.initialize(end.width(), end.height(), end.width(), end.height());
+ final int width = endBounds.width();
+ final int height = endBounds.height();
+ a.initialize(width, height, width, height);
}
a.restrictDuration(MAX_ANIMATION_DURATION);
a.scaleCurrentDuration(mTransitionAnimationScaleSetting);
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 68b0b4e48252..cb478c84c2b7 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
@@ -118,10 +118,10 @@ fun FlickerTestParameter.dockedStackSecondaryBoundsIsVisibleAtEnd(
fun getPrimaryRegion(dividerRegion: Region, rotation: Int): Region {
val displayBounds = WindowUtils.getDisplayBounds(rotation)
return if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180) {
- Region(0, 0, displayBounds.bounds.right,
+ Region.from(0, 0, displayBounds.bounds.right,
dividerRegion.bounds.top + WindowUtils.dockedStackDividerInset)
} else {
- Region(0, 0, dividerRegion.bounds.left + WindowUtils.dockedStackDividerInset,
+ Region.from(0, 0, dividerRegion.bounds.left + WindowUtils.dockedStackDividerInset,
displayBounds.bounds.bottom)
}
}
@@ -129,10 +129,10 @@ fun getPrimaryRegion(dividerRegion: Region, rotation: Int): Region {
fun getSecondaryRegion(dividerRegion: Region, rotation: Int): Region {
val displayBounds = WindowUtils.getDisplayBounds(rotation)
return if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180) {
- Region(0, dividerRegion.bounds.bottom - WindowUtils.dockedStackDividerInset,
+ Region.from(0, dividerRegion.bounds.bottom - WindowUtils.dockedStackDividerInset,
displayBounds.bounds.right, displayBounds.bounds.bottom)
} else {
- Region(dividerRegion.bounds.right - WindowUtils.dockedStackDividerInset, 0,
+ Region.from(dividerRegion.bounds.right - WindowUtils.dockedStackDividerInset, 0,
displayBounds.bounds.right, displayBounds.bounds.bottom)
}
} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/OWNERS b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/OWNERS
new file mode 100644
index 000000000000..8446b37dbf06
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/OWNERS
@@ -0,0 +1,2 @@
+# window manager > wm shell > Split Screen
+# Bug component: 928697
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ExpandBubbleScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ExpandBubbleScreen.kt
index af629cc9f8ee..f8d14c6e6d04 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ExpandBubbleScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ExpandBubbleScreen.kt
@@ -24,6 +24,9 @@ import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.annotation.Group4
import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.wm.shell.flicker.helpers.BaseAppHelper
+import org.junit.Assume
+import org.junit.Before
import org.junit.runner.RunWith
import org.junit.Test
import org.junit.runners.Parameterized
@@ -59,6 +62,12 @@ class ExpandBubbleScreen(testSpec: FlickerTestParameter) : BaseBubbleScreen(test
}
}
+ @Before
+ fun setup() {
+ // This test doesn't work in shell transitions because of b/205288792
+ Assume.assumeFalse(BaseAppHelper.isShellTransitionsEnabled())
+ }
+
@Presubmit
@Test
fun testAppIsAlwaysVisible() {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/MultiBubblesScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/MultiBubblesScreen.kt
index add11c10d75f..c93c5ad97bdb 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/MultiBubblesScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/MultiBubblesScreen.kt
@@ -25,6 +25,9 @@ import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.annotation.Group4
import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.wm.shell.flicker.helpers.BaseAppHelper
+import org.junit.Assume
+import org.junit.Before
import org.junit.runner.RunWith
import org.junit.Test
import org.junit.runners.Parameterized
@@ -67,6 +70,12 @@ class MultiBubblesScreen(testSpec: FlickerTestParameter) : BaseBubbleScreen(test
}
}
+ @Before
+ fun setup() {
+ // This test doesn't work in shell transitions because of b/205288792
+ Assume.assumeFalse(BaseAppHelper.isShellTransitionsEnabled())
+ }
+
@Presubmit
@Test
fun testAppIsAlwaysVisible() {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OWNERS b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OWNERS
new file mode 100644
index 000000000000..566acc87e42d
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OWNERS
@@ -0,0 +1,2 @@
+# window manager > wm shell > Bubbles
+# Bug component: 555586
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 efae20731bef..cf4ea467a29b 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
@@ -28,14 +28,14 @@ class AppPairsHelper(
component: FlickerComponentName
) : BaseAppHelper(instrumentation, activityLabel, component) {
fun getPrimaryBounds(dividerBounds: Region): Region {
- val primaryAppBounds = Region(0, 0, dividerBounds.bounds.right,
+ val primaryAppBounds = Region.from(0, 0, dividerBounds.bounds.right,
dividerBounds.bounds.bottom + WindowUtils.dockedStackDividerInset)
return primaryAppBounds
}
fun getSecondaryBounds(dividerBounds: Region): Region {
val displayBounds = WindowUtils.displayBounds
- val secondaryAppBounds = Region(0,
+ val secondaryAppBounds = Region.from(0,
dividerBounds.bounds.bottom - WindowUtils.dockedStackDividerInset,
displayBounds.right, displayBounds.bottom - WindowUtils.navigationBarHeight)
return secondaryAppBounds
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OWNERS b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OWNERS
new file mode 100644
index 000000000000..8446b37dbf06
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OWNERS
@@ -0,0 +1,2 @@
+# window manager > wm shell > Split Screen
+# Bug component: 928697
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 264d482426cb..a510d699387e 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
@@ -166,9 +166,9 @@ class ResizeLegacySplitScreen(
val dividerBounds =
layer(DOCKED_STACK_DIVIDER_COMPONENT).visibleRegion.region.bounds
- val topAppBounds = Region(0, 0, dividerBounds.right,
+ val topAppBounds = Region.from(0, 0, dividerBounds.right,
dividerBounds.top + WindowUtils.dockedStackDividerInset)
- val bottomAppBounds = Region(0,
+ val bottomAppBounds = Region.from(0,
dividerBounds.bottom - WindowUtils.dockedStackDividerInset,
displayBounds.right,
displayBounds.bottom - WindowUtils.navigationBarHeight)
@@ -187,9 +187,9 @@ class ResizeLegacySplitScreen(
val dividerBounds =
layer(DOCKED_STACK_DIVIDER_COMPONENT).visibleRegion.region.bounds
- val topAppBounds = Region(0, 0, dividerBounds.right,
+ val topAppBounds = Region.from(0, 0, dividerBounds.right,
dividerBounds.top + WindowUtils.dockedStackDividerInset)
- val bottomAppBounds = Region(0,
+ val bottomAppBounds = Region.from(0,
dividerBounds.bottom - WindowUtils.dockedStackDividerInset,
displayBounds.right,
displayBounds.bottom - WindowUtils.navigationBarHeight)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt
index 2c08b7f5fac2..3a9a0705908d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt
@@ -82,6 +82,11 @@ class ExitPipViaExpandButtonClickTest(
@Test
override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
+ /** {@inheritDoc} */
+ @FlakyTest(bugId = 197726610)
+ @Test
+ override fun pipLayerExpands() = super.pipLayerExpands()
+
companion object {
/**
* Creates the test configurations.
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt
index e340f4cd8595..03c8929f9919 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt
@@ -101,6 +101,11 @@ class ExitPipViaIntentTest(testSpec: FlickerTestParameter) : ExitPipToAppTransit
@Test
override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
+ /** {@inheritDoc} */
+ @FlakyTest(bugId = 197726610)
+ @Test
+ override fun pipLayerExpands() = super.pipLayerExpands()
+
companion object {
/**
* Creates the test configurations.
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt
index 8adebb8f28c9..976b7c6980a1 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt
@@ -90,6 +90,11 @@ class ExitPipWithDismissButtonTest(testSpec: FlickerTestParameter) : ExitPipTran
@Test
override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
+ /** {@inheritDoc} */
+ @FlakyTest(bugId = 215869110)
+ @Test
+ override fun focusDoesNotChange() = super.focusDoesNotChange()
+
companion object {
/**
* Creates the test configurations.
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/OWNERS b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/OWNERS
new file mode 100644
index 000000000000..172e24bf4574
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/OWNERS
@@ -0,0 +1,2 @@
+# window manager > wm shell > Picture-In-Picture
+# Bug component: 316251
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
new file mode 100644
index 000000000000..960c7ac4099a
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.back;
+
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.app.IActivityTaskManager;
+import android.app.WindowConfiguration;
+import android.hardware.HardwareBuffer;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+import android.testing.AndroidTestingRunner;
+import android.view.MotionEvent;
+import android.view.SurfaceControl;
+import android.window.BackNavigationInfo;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.TestShellExecutor;
+import com.android.wm.shell.common.ShellExecutor;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * atest WMShellUnitTests:BackAnimationControllerTest
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class BackAnimationControllerTest {
+
+ private final ShellExecutor mShellExecutor = new TestShellExecutor();
+
+ @Mock
+ private SurfaceControl.Transaction mTransaction;
+
+ @Mock
+ private IActivityTaskManager mActivityTaskManager;
+
+ private BackAnimationController mController;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mController = new BackAnimationController(
+ mShellExecutor, mTransaction, mActivityTaskManager);
+ }
+
+ private void createNavigationInfo(SurfaceControl topWindowLeash,
+ SurfaceControl screenshotSurface,
+ HardwareBuffer hardwareBuffer) {
+ BackNavigationInfo navigationInfo = new BackNavigationInfo(
+ BackNavigationInfo.TYPE_RETURN_TO_HOME,
+ topWindowLeash,
+ screenshotSurface,
+ hardwareBuffer,
+ new WindowConfiguration(),
+ new RemoteCallback((bundle) -> {}));
+ try {
+ doReturn(navigationInfo).when(mActivityTaskManager).startBackNavigation();
+ } catch (RemoteException ex) {
+ ex.rethrowFromSystemServer();
+ }
+ }
+
+ @Test
+ public void screenshotAttachedAndVisible() {
+ SurfaceControl topWindowLeash = new SurfaceControl();
+ SurfaceControl screenshotSurface = new SurfaceControl();
+ HardwareBuffer hardwareBuffer = mock(HardwareBuffer.class);
+ createNavigationInfo(topWindowLeash, screenshotSurface, hardwareBuffer);
+ mController.onMotionEvent(MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0));
+ verify(mTransaction).setBuffer(screenshotSurface, hardwareBuffer);
+ verify(mTransaction).setVisibility(screenshotSurface, true);
+ verify(mTransaction).apply();
+ }
+
+ @Test
+ public void surfaceMovesWithGesture() {
+ SurfaceControl topWindowLeash = new SurfaceControl();
+ SurfaceControl screenshotSurface = new SurfaceControl();
+ HardwareBuffer hardwareBuffer = mock(HardwareBuffer.class);
+ createNavigationInfo(topWindowLeash, screenshotSurface, hardwareBuffer);
+ mController.onMotionEvent(MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0));
+ mController.onMotionEvent(MotionEvent.obtain(10, 0, MotionEvent.ACTION_MOVE, 100, 100, 0));
+ verify(mTransaction).setPosition(topWindowLeash, 100, 100);
+ verify(mTransaction, atLeastOnce()).apply();
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
index fe66e225ad4a..35e498262707 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
@@ -19,7 +19,6 @@ package com.android.wm.shell.draganddrop;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.content.ClipDescription.MIMETYPE_APPLICATION_ACTIVITY;
import static android.content.ClipDescription.MIMETYPE_APPLICATION_SHORTCUT;
import static android.content.ClipDescription.MIMETYPE_APPLICATION_TASK;
@@ -111,7 +110,6 @@ public class DragAndDropPolicyTest {
private ActivityManager.RunningTaskInfo mHomeTask;
private ActivityManager.RunningTaskInfo mFullscreenAppTask;
private ActivityManager.RunningTaskInfo mNonResizeableFullscreenAppTask;
- private ActivityManager.RunningTaskInfo mSplitPrimaryAppTask;
@Before
public void setUp() throws RemoteException {
@@ -144,8 +142,6 @@ public class DragAndDropPolicyTest {
mNonResizeableFullscreenAppTask =
createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
mNonResizeableFullscreenAppTask.isResizeable = false;
- mSplitPrimaryAppTask = createTaskInfo(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY,
- ACTIVITY_TYPE_STANDARD);
setRunningTask(mFullscreenAppTask);
}
diff --git a/libs/androidfw/Locale.cpp b/libs/androidfw/Locale.cpp
index 3eedda88fdce..d87a3ce72177 100644
--- a/libs/androidfw/Locale.cpp
+++ b/libs/androidfw/Locale.cpp
@@ -29,40 +29,33 @@ using ::android::StringPiece;
namespace android {
-void LocaleValue::set_language(const char* language_chars) {
+template <size_t N, class Transformer>
+static void safe_transform_copy(const char* source, char (&dest)[N], Transformer t) {
size_t i = 0;
- while ((*language_chars) != '\0') {
- language[i++] = ::tolower(*language_chars);
- language_chars++;
+ while (i < N && (*source) != '\0') {
+ dest[i++] = t(i, *source);
+ source++;
+ }
+ while (i < N) {
+ dest[i++] = '\0';
}
}
+void LocaleValue::set_language(const char* language_chars) {
+ safe_transform_copy(language_chars, language, [](size_t, char c) { return ::tolower(c); });
+}
+
void LocaleValue::set_region(const char* region_chars) {
- size_t i = 0;
- while ((*region_chars) != '\0') {
- region[i++] = ::toupper(*region_chars);
- region_chars++;
- }
+ safe_transform_copy(region_chars, region, [](size_t, char c) { return ::toupper(c); });
}
void LocaleValue::set_script(const char* script_chars) {
- size_t i = 0;
- while ((*script_chars) != '\0') {
- if (i == 0) {
- script[i++] = ::toupper(*script_chars);
- } else {
- script[i++] = ::tolower(*script_chars);
- }
- script_chars++;
- }
+ safe_transform_copy(script_chars, script,
+ [](size_t i, char c) { return i ? ::tolower(c) : ::toupper(c); });
}
void LocaleValue::set_variant(const char* variant_chars) {
- size_t i = 0;
- while ((*variant_chars) != '\0') {
- variant[i++] = *variant_chars;
- variant_chars++;
- }
+ safe_transform_copy(variant_chars, variant, [](size_t, char c) { return c; });
}
static inline bool is_alpha(const std::string& str) {
@@ -234,6 +227,10 @@ ssize_t LocaleValue::InitFromParts(std::vector<std::string>::iterator iter,
return static_cast<ssize_t>(iter - start_iter);
}
+// Make sure the following memcpy's are properly sized.
+static_assert(sizeof(ResTable_config::localeScript) == sizeof(LocaleValue::script));
+static_assert(sizeof(ResTable_config::localeVariant) == sizeof(LocaleValue::variant));
+
void LocaleValue::InitFromResTable(const ResTable_config& config) {
config.unpackLanguage(language);
config.unpackRegion(region);
diff --git a/libs/hwui/jni/text/MeasuredText.cpp b/libs/hwui/jni/text/MeasuredText.cpp
index 09539ecc34b0..76ea2d5c9ff3 100644
--- a/libs/hwui/jni/text/MeasuredText.cpp
+++ b/libs/hwui/jni/text/MeasuredText.cpp
@@ -65,11 +65,13 @@ static jlong nInitBuilder(CRITICAL_JNI_PARAMS) {
// Regular JNI
static void nAddStyleRun(JNIEnv* /* unused */, jclass /* unused */, jlong builderPtr,
- jlong paintPtr, jint lbStyle, jint start, jint end, jboolean isRtl) {
+ jlong paintPtr, jint lbStyle, jint lbWordStyle, jint start, jint end,
+ jboolean isRtl) {
Paint* paint = toPaint(paintPtr);
const Typeface* typeface = Typeface::resolveDefault(paint->getAndroidTypeface());
minikin::MinikinPaint minikinPaint = MinikinUtils::prepareMinikinPaint(paint, typeface);
- toBuilder(builderPtr)->addStyleRun(start, end, std::move(minikinPaint), lbStyle, isRtl);
+ toBuilder(builderPtr)
+ ->addStyleRun(start, end, std::move(minikinPaint), lbStyle, lbWordStyle, isRtl);
}
// Regular JNI
@@ -144,7 +146,7 @@ static jint nGetMemoryUsage(CRITICAL_JNI_PARAMS_COMMA jlong ptr) {
static const JNINativeMethod gMTBuilderMethods[] = {
// MeasuredParagraphBuilder native functions.
{"nInitBuilder", "()J", (void*)nInitBuilder},
- {"nAddStyleRun", "(JJIIIZ)V", (void*)nAddStyleRun},
+ {"nAddStyleRun", "(JJIIIIZ)V", (void*)nAddStyleRun},
{"nAddReplacementRun", "(JJIIF)V", (void*)nAddReplacementRun},
{"nBuildMeasuredText", "(JJ[CZZZ)J", (void*)nBuildMeasuredText},
{"nFreeBuilder", "(J)V", (void*)nFreeBuilder},
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index d86237789b11..f627a3c656d1 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -117,7 +117,7 @@ void RenderThread::extendedFrameCallback(const AChoreographerFrameCallbackData*
RenderThread* rt = reinterpret_cast<RenderThread*>(data);
size_t preferredFrameTimelineIndex =
AChoreographerFrameCallbackData_getPreferredFrameTimelineIndex(cbData);
- int64_t vsyncId = AChoreographerFrameCallbackData_getFrameTimelineVsyncId(
+ AVsyncId vsyncId = AChoreographerFrameCallbackData_getFrameTimelineVsyncId(
cbData, preferredFrameTimelineIndex);
int64_t frameDeadline = AChoreographerFrameCallbackData_getFrameTimelineDeadlineNanos(
cbData, preferredFrameTimelineIndex);
diff --git a/libs/services/Android.bp b/libs/services/Android.bp
index bf2e764aae6a..f656ebfc3b77 100644
--- a/libs/services/Android.bp
+++ b/libs/services/Android.bp
@@ -27,6 +27,7 @@ cc_library_shared {
name: "libservices",
srcs: [
":IDropBoxManagerService.aidl",
+ ":ILogcatManagerService_aidl",
"src/content/ComponentName.cpp",
"src/os/DropBoxManager.cpp",
],
diff --git a/location/java/android/location/GnssMeasurement.java b/location/java/android/location/GnssMeasurement.java
index cdfa02c8b28f..ab3dafe9cec7 100644
--- a/location/java/android/location/GnssMeasurement.java
+++ b/location/java/android/location/GnssMeasurement.java
@@ -1868,7 +1868,7 @@ public final class GnssMeasurement implements Parcelable {
gnssMeasurement.mSatelliteInterSignalBiasUncertaintyNanos = parcel.readDouble();
if (gnssMeasurement.hasSatellitePvt()) {
ClassLoader classLoader = getClass().getClassLoader();
- gnssMeasurement.mSatellitePvt = parcel.readParcelable(classLoader);
+ gnssMeasurement.mSatellitePvt = parcel.readParcelable(classLoader, android.location.SatellitePvt.class);
}
if (gnssMeasurement.hasCorrelationVectors()) {
CorrelationVector[] correlationVectorsArray =
diff --git a/location/java/android/location/GnssMeasurementsEvent.java b/location/java/android/location/GnssMeasurementsEvent.java
index 075ddebc859f..0397740d104e 100644
--- a/location/java/android/location/GnssMeasurementsEvent.java
+++ b/location/java/android/location/GnssMeasurementsEvent.java
@@ -160,7 +160,7 @@ public final class GnssMeasurementsEvent implements Parcelable {
new Creator<GnssMeasurementsEvent>() {
@Override
public GnssMeasurementsEvent createFromParcel(Parcel in) {
- GnssClock clock = in.readParcelable(getClass().getClassLoader());
+ GnssClock clock = in.readParcelable(getClass().getClassLoader(), android.location.GnssClock.class);
List<GnssMeasurement> measurements = in.createTypedArrayList(GnssMeasurement.CREATOR);
List<GnssAutomaticGainControl> agcs = in.createTypedArrayList(
GnssAutomaticGainControl.CREATOR);
diff --git a/location/java/android/location/GpsMeasurementsEvent.java b/location/java/android/location/GpsMeasurementsEvent.java
index f3feb7a4c7b6..6b834f324839 100644
--- a/location/java/android/location/GpsMeasurementsEvent.java
+++ b/location/java/android/location/GpsMeasurementsEvent.java
@@ -112,7 +112,7 @@ public class GpsMeasurementsEvent implements Parcelable {
public GpsMeasurementsEvent createFromParcel(Parcel in) {
ClassLoader classLoader = getClass().getClassLoader();
- GpsClock clock = in.readParcelable(classLoader);
+ GpsClock clock = in.readParcelable(classLoader, android.location.GpsClock.class);
int measurementsLength = in.readInt();
GpsMeasurement[] measurementsArray = new GpsMeasurement[measurementsLength];
diff --git a/location/java/android/location/GpsNavigationMessageEvent.java b/location/java/android/location/GpsNavigationMessageEvent.java
index 2d5d6ebd5990..b37fe3dfb792 100644
--- a/location/java/android/location/GpsNavigationMessageEvent.java
+++ b/location/java/android/location/GpsNavigationMessageEvent.java
@@ -92,7 +92,7 @@ public class GpsNavigationMessageEvent implements Parcelable {
@Override
public GpsNavigationMessageEvent createFromParcel(Parcel in) {
ClassLoader classLoader = getClass().getClassLoader();
- GpsNavigationMessage navigationMessage = in.readParcelable(classLoader);
+ GpsNavigationMessage navigationMessage = in.readParcelable(classLoader, android.location.GpsNavigationMessage.class);
return new GpsNavigationMessageEvent(navigationMessage);
}
diff --git a/location/java/android/location/SatellitePvt.java b/location/java/android/location/SatellitePvt.java
index 794a8d0731f9..aa43cfd8711c 100644
--- a/location/java/android/location/SatellitePvt.java
+++ b/location/java/android/location/SatellitePvt.java
@@ -465,9 +465,9 @@ public final class SatellitePvt implements Parcelable {
public SatellitePvt createFromParcel(Parcel in) {
int flags = in.readInt();
ClassLoader classLoader = getClass().getClassLoader();
- PositionEcef positionEcef = in.readParcelable(classLoader);
- VelocityEcef velocityEcef = in.readParcelable(classLoader);
- ClockInfo clockInfo = in.readParcelable(classLoader);
+ PositionEcef positionEcef = in.readParcelable(classLoader, android.location.SatellitePvt.PositionEcef.class);
+ VelocityEcef velocityEcef = in.readParcelable(classLoader, android.location.SatellitePvt.VelocityEcef.class);
+ ClockInfo clockInfo = in.readParcelable(classLoader, android.location.SatellitePvt.ClockInfo.class);
double ionoDelayMeters = in.readDouble();
double tropoDelayMeters = in.readDouble();
diff --git a/media/aidl/android/media/audio/common/AudioContentType.aidl b/media/aidl/android/media/audio/common/AudioContentType.aidl
index 50ac181adcb2..f42ae2fedc52 100644
--- a/media/aidl/android/media/audio/common/AudioContentType.aidl
+++ b/media/aidl/android/media/audio/common/AudioContentType.aidl
@@ -50,4 +50,8 @@ enum AudioContentType {
* in a game. These sounds are mostly synthesized or short Foley sounds.
*/
SONIFICATION = 4,
+ /**
+ * Content type value to use when the content type is ultrasound.
+ */
+ ULTRASOUND = 1997,
}
diff --git a/media/aidl/android/media/audio/common/AudioDeviceType.aidl b/media/aidl/android/media/audio/common/AudioDeviceType.aidl
index afe6d105eb1b..8e200de34e3d 100644
--- a/media/aidl/android/media/audio/common/AudioDeviceType.aidl
+++ b/media/aidl/android/media/audio/common/AudioDeviceType.aidl
@@ -168,4 +168,8 @@ enum AudioDeviceType {
* Output into a speaker of a phone / table dock.
*/
OUT_DOCK = 145,
+ /**
+ * Output to a broadcast group.
+ */
+ OUT_BROADCAST = 146,
}
diff --git a/media/aidl/android/media/audio/common/AudioInputFlags.aidl b/media/aidl/android/media/audio/common/AudioInputFlags.aidl
index e4b6ec242ef4..83a5d9dc98e4 100644
--- a/media/aidl/android/media/audio/common/AudioInputFlags.aidl
+++ b/media/aidl/android/media/audio/common/AudioInputFlags.aidl
@@ -59,4 +59,8 @@ enum AudioInputFlags {
* Input contains an encoded audio stream.
*/
DIRECT = 7,
+ /**
+ * Input is for capturing "ultrasound" audio commands.
+ */
+ ULTRASOUND = 8,
}
diff --git a/media/aidl/android/media/audio/common/AudioOutputFlags.aidl b/media/aidl/android/media/audio/common/AudioOutputFlags.aidl
index 050503647d33..2556b68809cc 100644
--- a/media/aidl/android/media/audio/common/AudioOutputFlags.aidl
+++ b/media/aidl/android/media/audio/common/AudioOutputFlags.aidl
@@ -26,7 +26,7 @@ package android.media.audio.common;
*/
@VintfStability
@Backing(type="int")
-enum AudioOutputFlags {
+enum AudioOutputFlags{
/**
* Output must not be altered by the framework, it bypasses software mixers.
*/
@@ -97,4 +97,12 @@ enum AudioOutputFlags {
* tracks.
*/
GAPLESS_OFFLOAD = 15,
+ /**
+ * Output is used for spatial audio.
+ */
+ SPATIALIZER = 16,
+ /**
+ * Output is used for transmitting ultrasound audio.
+ */
+ ULTRASOUND = 17,
}
diff --git a/media/aidl/android/media/audio/common/AudioSource.aidl b/media/aidl/android/media/audio/common/AudioSource.aidl
index 527ee39b3267..77799946c04a 100644
--- a/media/aidl/android/media/audio/common/AudioSource.aidl
+++ b/media/aidl/android/media/audio/common/AudioSource.aidl
@@ -87,4 +87,8 @@ enum AudioSource {
* hotword detection. Same tuning as VOICE_RECOGNITION.
*/
HOTWORD = 1999,
+ /** Microphone audio source for ultrasound sound if available,
+ * behaves like DEFAULT otherwise.
+ */
+ ULTRASOUND = 2000,
}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioContentType.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioContentType.aidl
index 3798b8253766..f9ac61426fa3 100644
--- a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioContentType.aidl
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioContentType.aidl
@@ -40,4 +40,5 @@ enum AudioContentType {
MUSIC = 2,
MOVIE = 3,
SONIFICATION = 4,
+ ULTRASOUND = 1997,
}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioDeviceType.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioDeviceType.aidl
index 0b7b77ca8718..6a7b6864b801 100644
--- a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioDeviceType.aidl
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioDeviceType.aidl
@@ -67,4 +67,5 @@ enum AudioDeviceType {
OUT_SUBMIX = 143,
OUT_TELEPHONY_TX = 144,
OUT_DOCK = 145,
+ OUT_BROADCAST = 146,
}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioInputFlags.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioInputFlags.aidl
index 8a5dae0cc612..37aa64ae4cee 100644
--- a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioInputFlags.aidl
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioInputFlags.aidl
@@ -43,4 +43,5 @@ enum AudioInputFlags {
VOIP_TX = 5,
HW_AV_SYNC = 6,
DIRECT = 7,
+ ULTRASOUND = 8,
}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioOutputFlags.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioOutputFlags.aidl
index ed16d177e18c..4a512a8049a2 100644
--- a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioOutputFlags.aidl
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioOutputFlags.aidl
@@ -51,4 +51,6 @@ enum AudioOutputFlags {
VOIP_RX = 13,
INCALL_MUSIC = 14,
GAPLESS_OFFLOAD = 15,
+ SPATIALIZER = 16,
+ ULTRASOUND = 17,
}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioSource.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioSource.aidl
index d1dfe416d692..acf822e5e0a5 100644
--- a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioSource.aidl
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioSource.aidl
@@ -50,4 +50,5 @@ enum AudioSource {
ECHO_REFERENCE = 1997,
FM_TUNER = 1998,
HOTWORD = 1999,
+ ULTRASOUND = 2000,
}
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index 85e49cc5430b..ded9597b68ef 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -101,6 +101,13 @@ public final class AudioAttributes implements Parcelable {
* or short Foley sounds.
*/
public final static int CONTENT_TYPE_SONIFICATION = 4;
+ /**
+ * @hide
+ * Content type value to use when the content type is ultrasound.
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.ACCESS_ULTRASOUND)
+ public static final int CONTENT_TYPE_ULTRASOUND = 1997;
/**
* Invalid value, only ever used for an uninitialized usage value
@@ -958,6 +965,26 @@ public final class AudioAttributes implements Parcelable {
}
/**
+ * @hide
+ * Sets the attribute describing the content type of the audio signal, such as speech,
+ * , music or ultrasound.
+ * @param contentType the content type values.
+ * @return the same Builder instance.
+ */
+ @SystemApi
+ public @NonNull Builder setInternalContentType(@AttrInternalContentType int contentType) {
+ switch (contentType) {
+ case CONTENT_TYPE_ULTRASOUND:
+ mContentType = contentType;
+ break;
+ default:
+ setContentType(contentType);
+ break;
+ }
+ return this;
+ }
+
+ /**
* Sets the combination of flags.
*
* This is a bitwise OR with the existing flags.
@@ -1234,7 +1261,8 @@ public final class AudioAttributes implements Parcelable {
/**
* @hide
* Same as {@link #setCapturePreset(int)} but authorizes the use of HOTWORD,
- * REMOTE_SUBMIX, RADIO_TUNER, VOICE_DOWNLINK, VOICE_UPLINK, VOICE_CALL and ECHO_REFERENCE.
+ * REMOTE_SUBMIX, RADIO_TUNER, VOICE_DOWNLINK, VOICE_UPLINK, VOICE_CALL, ECHO_REFERENCE
+ * and ULTRASOUND
* @param preset
* @return the same Builder instance.
*/
@@ -1246,7 +1274,8 @@ public final class AudioAttributes implements Parcelable {
|| (preset == MediaRecorder.AudioSource.VOICE_DOWNLINK)
|| (preset == MediaRecorder.AudioSource.VOICE_UPLINK)
|| (preset == MediaRecorder.AudioSource.VOICE_CALL)
- || (preset == MediaRecorder.AudioSource.ECHO_REFERENCE)) {
+ || (preset == MediaRecorder.AudioSource.ECHO_REFERENCE)
+ || (preset == MediaRecorder.AudioSource.ULTRASOUND)) {
mSource = preset;
} else {
setCapturePreset(preset);
@@ -1589,6 +1618,7 @@ public final class AudioAttributes implements Parcelable {
case CONTENT_TYPE_MUSIC: return new String("CONTENT_TYPE_MUSIC");
case CONTENT_TYPE_MOVIE: return new String("CONTENT_TYPE_MOVIE");
case CONTENT_TYPE_SONIFICATION: return new String("CONTENT_TYPE_SONIFICATION");
+ case CONTENT_TYPE_ULTRASOUND: return new String("CONTENT_TYPE_ULTRASOUND");
default: return new String("unknown content type " + mContentType);
}
}
@@ -1823,4 +1853,16 @@ public final class AudioAttributes implements Parcelable {
})
@Retention(RetentionPolicy.SOURCE)
public @interface AttributeContentType {}
+
+ /** @hide */
+ @IntDef({
+ CONTENT_TYPE_UNKNOWN,
+ CONTENT_TYPE_SPEECH,
+ CONTENT_TYPE_MUSIC,
+ CONTENT_TYPE_MOVIE,
+ CONTENT_TYPE_SONIFICATION,
+ CONTENT_TYPE_ULTRASOUND
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface AttrInternalContentType {}
}
diff --git a/media/java/android/media/AudioDeviceInfo.java b/media/java/android/media/AudioDeviceInfo.java
index 211a50ee93af..dd17dc649bc7 100644
--- a/media/java/android/media/AudioDeviceInfo.java
+++ b/media/java/android/media/AudioDeviceInfo.java
@@ -177,6 +177,11 @@ public final class AudioDeviceInfo {
*/
public static final int TYPE_HDMI_EARC = 29;
+ /**
+ * A device type describing a Bluetooth Low Energy (BLE) broadcast group.
+ */
+ public static final int TYPE_BLE_BROADCAST = 30;
+
/** @hide */
@IntDef(flag = false, prefix = "TYPE", value = {
TYPE_BUILTIN_EARPIECE,
@@ -207,7 +212,8 @@ public final class AudioDeviceInfo {
TYPE_REMOTE_SUBMIX,
TYPE_BLE_HEADSET,
TYPE_BLE_SPEAKER,
- TYPE_ECHO_REFERENCE}
+ TYPE_ECHO_REFERENCE,
+ TYPE_BLE_BROADCAST}
)
@Retention(RetentionPolicy.SOURCE)
public @interface AudioDeviceType {}
@@ -264,7 +270,8 @@ public final class AudioDeviceInfo {
TYPE_HEARING_AID,
TYPE_BUILTIN_SPEAKER_SAFE,
TYPE_BLE_HEADSET,
- TYPE_BLE_SPEAKER}
+ TYPE_BLE_SPEAKER,
+ TYPE_BLE_BROADCAST}
)
@Retention(RetentionPolicy.SOURCE)
public @interface AudioDeviceTypeOut {}
@@ -296,6 +303,7 @@ public final class AudioDeviceInfo {
case TYPE_BUILTIN_SPEAKER_SAFE:
case TYPE_BLE_HEADSET:
case TYPE_BLE_SPEAKER:
+ case TYPE_BLE_BROADCAST:
return true;
default:
return false;
@@ -636,6 +644,7 @@ public final class AudioDeviceInfo {
INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_OUT_REMOTE_SUBMIX, TYPE_REMOTE_SUBMIX);
INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_OUT_BLE_HEADSET, TYPE_BLE_HEADSET);
INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_OUT_BLE_SPEAKER, TYPE_BLE_SPEAKER);
+ INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_OUT_BLE_BROADCAST, TYPE_BLE_BROADCAST);
INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_IN_BUILTIN_MIC, TYPE_BUILTIN_MIC);
INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_IN_BLUETOOTH_SCO_HEADSET, TYPE_BLUETOOTH_SCO);
@@ -690,6 +699,7 @@ public final class AudioDeviceInfo {
EXT_TO_INT_DEVICE_MAPPING.put(TYPE_REMOTE_SUBMIX, AudioSystem.DEVICE_OUT_REMOTE_SUBMIX);
EXT_TO_INT_DEVICE_MAPPING.put(TYPE_BLE_HEADSET, AudioSystem.DEVICE_OUT_BLE_HEADSET);
EXT_TO_INT_DEVICE_MAPPING.put(TYPE_BLE_SPEAKER, AudioSystem.DEVICE_OUT_BLE_SPEAKER);
+ EXT_TO_INT_DEVICE_MAPPING.put(TYPE_BLE_BROADCAST, AudioSystem.DEVICE_OUT_BLE_BROADCAST);
// privileges mapping to input device
EXT_TO_INT_INPUT_DEVICE_MAPPING = new SparseIntArray();
diff --git a/media/java/android/media/AudioDevicePort.java b/media/java/android/media/AudioDevicePort.java
index ebe08822a0c9..9211c53a17bc 100644
--- a/media/java/android/media/AudioDevicePort.java
+++ b/media/java/android/media/AudioDevicePort.java
@@ -90,7 +90,8 @@ public class AudioDevicePort extends AudioPort {
* {@link AudioManager#DEVICE_OUT_BLE_HEADSET}, {@link AudioManager#DEVICE_OUT_BLE_SPEAKER})
* use the MAC address of the bluetooth device in the form "00:11:22:AA:BB:CC" as reported by
* {@link BluetoothDevice#getAddress()}.
- * - Deivces that do not have an address will indicate an empty string "".
+ * - Bluetooth LE broadcast group ({@link AudioManager#DEVICE_OUT_BLE_BROADCAST} use the group number.
+ * - Devices that do not have an address will indicate an empty string "".
*/
public String address() {
return mAddress;
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 68e5d94f7559..c4cef4c77835 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -5440,6 +5440,10 @@ public class AudioManager {
*/
public static final int DEVICE_OUT_BLE_SPEAKER = AudioSystem.DEVICE_OUT_BLE_SPEAKER;
/** @hide
+ * The audio output device code for a BLE audio brodcast group.
+ */
+ public static final int DEVICE_OUT_BLE_BROADCAST = AudioSystem.DEVICE_OUT_BLE_BROADCAST;
+ /** @hide
* This is not used as a returned value from {@link #getDevicesForStream}, but could be
* used in the future in a set method to select whatever default device is chosen by the
* platform-specific implementation.
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index e76bb42560f6..52838898146d 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -1033,11 +1033,12 @@ public class AudioRecord implements AudioRouting, MicrophoneDirection,
//--------------
// audio source
- if ( (audioSource < MediaRecorder.AudioSource.DEFAULT) ||
- ((audioSource > MediaRecorder.getAudioSourceMax()) &&
- (audioSource != MediaRecorder.AudioSource.RADIO_TUNER) &&
- (audioSource != MediaRecorder.AudioSource.ECHO_REFERENCE) &&
- (audioSource != MediaRecorder.AudioSource.HOTWORD)) ) {
+ if ((audioSource < MediaRecorder.AudioSource.DEFAULT)
+ || ((audioSource > MediaRecorder.getAudioSourceMax())
+ && (audioSource != MediaRecorder.AudioSource.RADIO_TUNER)
+ && (audioSource != MediaRecorder.AudioSource.ECHO_REFERENCE)
+ && (audioSource != MediaRecorder.AudioSource.HOTWORD)
+ && (audioSource != MediaRecorder.AudioSource.ULTRASOUND))) {
throw new IllegalArgumentException("Invalid audio source " + audioSource);
}
mRecordSource = audioSource;
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index af5a3da5f3e2..306479a5b819 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -249,7 +249,8 @@ public class AudioSystem
AUDIO_FORMAT_SBC,
AUDIO_FORMAT_APTX,
AUDIO_FORMAT_APTX_HD,
- AUDIO_FORMAT_LDAC}
+ AUDIO_FORMAT_LDAC,
+ AUDIO_FORMAT_LC3}
)
@Retention(RetentionPolicy.SOURCE)
public @interface AudioFormatNativeEnumForBtCodec {}
@@ -281,6 +282,7 @@ public class AudioSystem
case AUDIO_FORMAT_APTX: return BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX;
case AUDIO_FORMAT_APTX_HD: return BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD;
case AUDIO_FORMAT_LDAC: return BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC;
+ case AUDIO_FORMAT_LC3: return BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3;
default:
Log.e(TAG, "Unknown audio format 0x" + Integer.toHexString(audioFormat)
+ " for conversion to BT codec");
@@ -321,6 +323,8 @@ public class AudioSystem
return AudioSystem.AUDIO_FORMAT_APTX_HD;
case BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC:
return AudioSystem.AUDIO_FORMAT_LDAC;
+ case BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3:
+ return AudioSystem.AUDIO_FORMAT_LC3;
default:
Log.e(TAG, "Unknown BT codec 0x" + Integer.toHexString(btCodec)
+ " for conversion to audio format");
@@ -421,6 +425,8 @@ public class AudioSystem
return "AUDIO_FORMAT_LHDC_LL";
case /* AUDIO_FORMAT_APTX_TWSP */ 0x2A000000:
return "AUDIO_FORMAT_APTX_TWSP";
+ case /* AUDIO_FORMAT_LC3 */ 0x2B000000:
+ return "AUDIO_FORMAT_LC3";
/* Aliases */
case /* AUDIO_FORMAT_PCM_16_BIT */ 0x1:
@@ -983,6 +989,8 @@ public class AudioSystem
public static final int DEVICE_OUT_BLE_HEADSET = 0x20000000;
/** @hide */
public static final int DEVICE_OUT_BLE_SPEAKER = 0x20000001;
+ /** @hide */
+ public static final int DEVICE_OUT_BLE_BROADCAST = 0x20000002;
/** @hide */
public static final int DEVICE_OUT_DEFAULT = DEVICE_BIT_DEFAULT;
@@ -1043,6 +1051,7 @@ public class AudioSystem
DEVICE_OUT_ALL_SET.add(DEVICE_OUT_ECHO_CANCELLER);
DEVICE_OUT_ALL_SET.add(DEVICE_OUT_BLE_HEADSET);
DEVICE_OUT_ALL_SET.add(DEVICE_OUT_BLE_SPEAKER);
+ DEVICE_OUT_ALL_SET.add(DEVICE_OUT_BLE_BROADCAST);
DEVICE_OUT_ALL_SET.add(DEVICE_OUT_DEFAULT);
DEVICE_OUT_ALL_A2DP_SET = new HashSet<>();
@@ -1073,6 +1082,7 @@ public class AudioSystem
DEVICE_OUT_ALL_BLE_SET = new HashSet<>();
DEVICE_OUT_ALL_BLE_SET.add(DEVICE_OUT_BLE_HEADSET);
DEVICE_OUT_ALL_BLE_SET.add(DEVICE_OUT_BLE_SPEAKER);
+ DEVICE_OUT_ALL_BLE_SET.add(DEVICE_OUT_BLE_BROADCAST);
}
// input devices
@@ -1256,6 +1266,7 @@ public class AudioSystem
/** @hide */ public static final String DEVICE_OUT_ECHO_CANCELLER_NAME = "echo_canceller";
/** @hide */ public static final String DEVICE_OUT_BLE_HEADSET_NAME = "ble_headset";
/** @hide */ public static final String DEVICE_OUT_BLE_SPEAKER_NAME = "ble_speaker";
+ /** @hide */ public static final String DEVICE_OUT_BLE_BROADCAST_NAME = "ble_broadcast";
/** @hide */ public static final String DEVICE_IN_COMMUNICATION_NAME = "communication";
/** @hide */ public static final String DEVICE_IN_AMBIENT_NAME = "ambient";
@@ -1355,6 +1366,8 @@ public class AudioSystem
return DEVICE_OUT_BLE_HEADSET_NAME;
case DEVICE_OUT_BLE_SPEAKER:
return DEVICE_OUT_BLE_SPEAKER_NAME;
+ case DEVICE_OUT_BLE_BROADCAST:
+ return DEVICE_OUT_BLE_BROADCAST_NAME;
case DEVICE_OUT_DEFAULT:
default:
return "0x" + Integer.toHexString(device);
diff --git a/media/java/android/media/ImageWriter.java b/media/java/android/media/ImageWriter.java
index 1fc2cf9edc90..6168c221bf6e 100644
--- a/media/java/android/media/ImageWriter.java
+++ b/media/java/android/media/ImageWriter.java
@@ -18,12 +18,16 @@ package android.media;
import android.annotation.IntRange;
import android.annotation.NonNull;
+import android.annotation.SuppressLint;
import android.graphics.GraphicBuffer;
import android.graphics.ImageFormat;
import android.graphics.ImageFormat.Format;
import android.graphics.PixelFormat;
import android.graphics.Rect;
+import android.hardware.DataSpace;
+import android.hardware.DataSpace.NamedDataSpace;
import android.hardware.HardwareBuffer;
+import android.hardware.HardwareBuffer.Usage;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.hardware.camera2.utils.SurfaceUtils;
import android.os.Handler;
@@ -95,10 +99,18 @@ public class ImageWriter implements AutoCloseable {
private ListenerHandler mListenerHandler;
private long mNativeContext;
+ private int mWidth;
+ private int mHeight;
+ private final int mMaxImages;
+ private @Usage long mUsage = HardwareBuffer.USAGE_CPU_WRITE_OFTEN;
+ private @HardwareBuffer.Format int mHardwareBufferFormat;
+ private @NamedDataSpace long mDataSpace;
+ private boolean mUseLegacyImageFormat;
+ private boolean mUseSurfaceImageFormatInfo;
+
// Field below is used by native code, do not access or modify.
private int mWriterFormat;
- private final int mMaxImages;
// Keep track of the currently dequeued Image. This need to be thread safe as the images
// could be closed by different threads (e.g., application thread and GC thread).
private List<Image> mDequeuedImages = new CopyOnWriteArrayList<>();
@@ -131,7 +143,7 @@ public class ImageWriter implements AutoCloseable {
*/
public static @NonNull ImageWriter newInstance(@NonNull Surface surface,
@IntRange(from = 1) int maxImages) {
- return new ImageWriter(surface, maxImages, ImageFormat.UNKNOWN, -1 /*width*/,
+ return new ImageWriter(surface, maxImages, true, ImageFormat.UNKNOWN, -1 /*width*/,
-1 /*height*/);
}
@@ -183,7 +195,7 @@ public class ImageWriter implements AutoCloseable {
if (!ImageFormat.isPublicFormat(format) && !PixelFormat.isPublicFormat(format)) {
throw new IllegalArgumentException("Invalid format is specified: " + format);
}
- return new ImageWriter(surface, maxImages, format, width, height);
+ return new ImageWriter(surface, maxImages, false, format, width, height);
}
/**
@@ -232,48 +244,49 @@ public class ImageWriter implements AutoCloseable {
if (!ImageFormat.isPublicFormat(format) && !PixelFormat.isPublicFormat(format)) {
throw new IllegalArgumentException("Invalid format is specified: " + format);
}
- return new ImageWriter(surface, maxImages, format, -1 /*width*/, -1 /*height*/);
+ return new ImageWriter(surface, maxImages, false, format, -1 /*width*/, -1 /*height*/);
}
- /**
- * @hide
- */
- protected ImageWriter(Surface surface, int maxImages, int format, int width, int height) {
+ private void initializeImageWriter(Surface surface, int maxImages,
+ boolean useSurfaceImageFormatInfo, boolean useLegacyImageFormat, int imageFormat,
+ int hardwareBufferFormat, long dataSpace, int width, int height, long usage) {
if (surface == null || maxImages < 1) {
throw new IllegalArgumentException("Illegal input argument: surface " + surface
- + ", maxImages: " + maxImages);
+ + ", maxImages: " + maxImages);
}
- mMaxImages = maxImages;
-
+ mUseSurfaceImageFormatInfo = useSurfaceImageFormatInfo;
+ mUseLegacyImageFormat = useLegacyImageFormat;
// Note that the underlying BufferQueue is working in synchronous mode
// to avoid dropping any buffers.
- mNativeContext = nativeInit(new WeakReference<>(this), surface, maxImages, format, width,
- height);
-
- // nativeInit internally overrides UNKNOWN format. So does surface format query after
- // nativeInit and before getEstimatedNativeAllocBytes().
- if (format == ImageFormat.UNKNOWN) {
- format = SurfaceUtils.getSurfaceFormat(surface);
- }
- // Several public formats use the same native HAL_PIXEL_FORMAT_BLOB. The native
- // allocation estimation sequence depends on the public formats values. To avoid
- // possible errors, convert where necessary.
- if (format == StreamConfigurationMap.HAL_PIXEL_FORMAT_BLOB) {
- int surfaceDataspace = SurfaceUtils.getSurfaceDataspace(surface);
- switch (surfaceDataspace) {
- case StreamConfigurationMap.HAL_DATASPACE_DEPTH:
- format = ImageFormat.DEPTH_POINT_CLOUD;
- break;
- case StreamConfigurationMap.HAL_DATASPACE_DYNAMIC_DEPTH:
- format = ImageFormat.DEPTH_JPEG;
- break;
- case StreamConfigurationMap.HAL_DATASPACE_HEIF:
- format = ImageFormat.HEIC;
- break;
- default:
- format = ImageFormat.JPEG;
+ mNativeContext = nativeInit(new WeakReference<>(this), surface, maxImages, width, height,
+ useSurfaceImageFormatInfo, hardwareBufferFormat, dataSpace, usage);
+
+ if (useSurfaceImageFormatInfo) {
+ // nativeInit internally overrides UNKNOWN format. So does surface format query after
+ // nativeInit and before getEstimatedNativeAllocBytes().
+ imageFormat = SurfaceUtils.getSurfaceFormat(surface);
+ // Several public formats use the same native HAL_PIXEL_FORMAT_BLOB. The native
+ // allocation estimation sequence depends on the public formats values. To avoid
+ // possible errors, convert where necessary.
+ if (imageFormat == StreamConfigurationMap.HAL_PIXEL_FORMAT_BLOB) {
+ int surfaceDataspace = SurfaceUtils.getSurfaceDataspace(surface);
+ switch (surfaceDataspace) {
+ case StreamConfigurationMap.HAL_DATASPACE_DEPTH:
+ imageFormat = ImageFormat.DEPTH_POINT_CLOUD;
+ break;
+ case StreamConfigurationMap.HAL_DATASPACE_DYNAMIC_DEPTH:
+ imageFormat = ImageFormat.DEPTH_JPEG;
+ break;
+ case StreamConfigurationMap.HAL_DATASPACE_HEIF:
+ imageFormat = ImageFormat.HEIC;
+ break;
+ default:
+ imageFormat = ImageFormat.JPEG;
+ }
}
+ mHardwareBufferFormat = PublicFormatUtils.getHalFormat(imageFormat);
+ mDataSpace = PublicFormatUtils.getHalDataspace(imageFormat);
}
// Estimate the native buffer allocation size and register it so it gets accounted for
// during GC. Note that this doesn't include the buffers required by the buffer queue
@@ -282,12 +295,49 @@ public class ImageWriter implements AutoCloseable {
// complex, and 1 buffer is enough for the VM to treat the ImageWriter as being of some
// size.
Size surfSize = SurfaceUtils.getSurfaceSize(surface);
+ mWidth = width == -1 ? surfSize.getWidth() : width;
+ mHeight = height == -1 ? surfSize.getHeight() : height;
+
mEstimatedNativeAllocBytes =
- ImageUtils.getEstimatedNativeAllocBytes(surfSize.getWidth(),surfSize.getHeight(),
- format, /*buffer count*/ 1);
+ ImageUtils.getEstimatedNativeAllocBytes(mWidth, mHeight,
+ useLegacyImageFormat ? imageFormat : hardwareBufferFormat, /*buffer count*/ 1);
VMRuntime.getRuntime().registerNativeAllocation(mEstimatedNativeAllocBytes);
}
+ private ImageWriter(Surface surface, int maxImages, boolean useSurfaceImageFormatInfo,
+ int imageFormat, int width, int height) {
+ mMaxImages = maxImages;
+ // update hal format and dataspace only if image format is overridden by producer.
+ mHardwareBufferFormat = PublicFormatUtils.getHalFormat(imageFormat);
+ mDataSpace = PublicFormatUtils.getHalDataspace(imageFormat);
+
+ initializeImageWriter(surface, maxImages, useSurfaceImageFormatInfo, true,
+ imageFormat, mHardwareBufferFormat, mDataSpace, width, height, mUsage);
+ }
+
+ private ImageWriter(Surface surface, int maxImages, boolean useSurfaceImageFormatInfo,
+ int imageFormat, int width, int height, long usage) {
+ mMaxImages = maxImages;
+ mUsage = usage;
+ mHardwareBufferFormat = PublicFormatUtils.getHalFormat(imageFormat);
+ mDataSpace = PublicFormatUtils.getHalDataspace(imageFormat);
+
+ initializeImageWriter(surface, maxImages, useSurfaceImageFormatInfo, true,
+ imageFormat, mHardwareBufferFormat, mDataSpace, width, height, usage);
+ }
+
+ private ImageWriter(Surface surface, int maxImages, boolean useSurfaceImageFormatInfo,
+ int hardwareBufferFormat, long dataSpace, int width, int height, long usage) {
+ mMaxImages = maxImages;
+ mUsage = usage;
+ mHardwareBufferFormat = hardwareBufferFormat;
+ mDataSpace = dataSpace;
+ int publicFormat = PublicFormatUtils.getPublicFormat(hardwareBufferFormat, dataSpace);
+
+ initializeImageWriter(surface, maxImages, useSurfaceImageFormatInfo, false,
+ publicFormat, hardwareBufferFormat, dataSpace, width, height, usage);
+ }
+
/**
* <p>
* Maximum number of Images that can be dequeued from the ImageWriter
@@ -316,6 +366,30 @@ public class ImageWriter implements AutoCloseable {
}
/**
+ * The width of {@link Image Images}, in pixels.
+ *
+ * <p>If {@link Builder#setWidthAndHeight} is not called, the default width of the Image
+ * depends on the Surface provided by customer end-point.</p>
+ *
+ * @return the expected actual width of an Image.
+ */
+ public int getWidth() {
+ return mWidth;
+ }
+
+ /**
+ * The height of {@link Image Images}, in pixels.
+ *
+ * <p>If {@link Builder#setWidthAndHeight} is not called, the default height of the Image
+ * depends on the Surface provided by customer end-point.</p>
+ *
+ * @return the expected height of an Image.
+ */
+ public int getHeight() {
+ return mHeight;
+ }
+
+ /**
* <p>
* Dequeue the next available input Image for the application to produce
* data into.
@@ -490,6 +564,41 @@ public class ImageWriter implements AutoCloseable {
}
/**
+ * Get the ImageWriter usage flag.
+ *
+ * @return The ImageWriter usage flag.
+ */
+ public @Usage long getUsage() {
+ return mUsage;
+ }
+
+ /**
+ * Get the ImageWriter hardwareBuffer format.
+ *
+ * <p>Use this function if the ImageWriter instance is created by builder pattern
+ * {@code ImageWriter.Builder} and using {@link Builder#setHardwareBufferFormat} and
+ * {@link Builder#setDataSpace}.</p>
+ *
+ * @return The ImageWriter hardwareBuffer format.
+ */
+ public @HardwareBuffer.Format int getHardwareBufferFormat() {
+ return mHardwareBufferFormat;
+ }
+
+ /**
+ * Get the ImageWriter dataspace.
+ *
+ * <p>Use this function if the ImageWriter instance is created by builder pattern
+ * {@code ImageWriter.Builder} and {@link Builder#setDataSpace}.</p>
+ *
+ * @return The ImageWriter dataspace.
+ */
+ @SuppressLint("MethodNameUnits")
+ public @NamedDataSpace long getDataSpace() {
+ return mDataSpace;
+ }
+
+ /**
* ImageWriter callback interface, used to to asynchronously notify the
* application of various ImageWriter events.
*/
@@ -755,6 +864,155 @@ public class ImageWriter implements AutoCloseable {
return true;
}
+ /**
+ * Builder class for {@link ImageWriter} objects.
+ */
+ public static final class Builder {
+ private Surface mSurface;
+ private int mWidth = -1;
+ private int mHeight = -1;
+ private int mMaxImages = 1;
+ private int mImageFormat = ImageFormat.UNKNOWN;
+ private @Usage long mUsage = HardwareBuffer.USAGE_CPU_WRITE_OFTEN;
+ private @HardwareBuffer.Format int mHardwareBufferFormat = HardwareBuffer.RGBA_8888;
+ private @NamedDataSpace long mDataSpace = DataSpace.DATASPACE_UNKNOWN;
+ private boolean mUseSurfaceImageFormatInfo = true;
+ // set this as true temporarily now as a workaround to get correct format
+ // when using surface format by default without overriding the image format
+ // in the builder pattern
+ private boolean mUseLegacyImageFormat = true;
+
+ /**
+ * Constructs a new builder for {@link ImageWriter}.
+ *
+ * @param surface The destination Surface this writer produces Image data into.
+ */
+ public Builder(@NonNull Surface surface) {
+ mSurface = surface;
+ }
+
+ /**
+ * Set the width and height of images. Default size is dependent on the Surface that is
+ * provided by the downstream end-point.
+ *
+ * @param width The width in pixels that will be passed to the producer.
+ * @param height The height in pixels that will be passed to the producer.
+ * @return the Builder instance with customized width and height.
+ */
+ @SuppressLint("MissingGetterMatchingBuilder")
+ public @NonNull Builder setWidthAndHeight(@IntRange(from = 1) int width,
+ @IntRange(from = 1) int height) {
+ mWidth = width;
+ mHeight = height;
+ return this;
+ }
+
+ /**
+ * Set the maximum number of images. Default value is 1.
+ *
+ * @param maxImages The maximum number of Images the user will want to access simultaneously
+ * for producing Image data.
+ * @return the Builder instance with customized usage value.
+ */
+ public @NonNull Builder setMaxImages(@IntRange(from = 1) int maxImages) {
+ mMaxImages = maxImages;
+ return this;
+ }
+
+ /**
+ * Set the image format of this ImageWriter.
+ * Default format depends on the Surface provided.
+ *
+ * @param imageFormat The format of the {@link ImageWriter}. It can be any valid specified
+ * by {@link ImageFormat} or {@link PixelFormat}.
+ * @return the Builder instance with customized image format.
+ */
+ @SuppressLint("MissingGetterMatchingBuilder")
+ public @NonNull Builder setImageFormat(@Format int imageFormat) {
+ if (!ImageFormat.isPublicFormat(imageFormat)
+ && !PixelFormat.isPublicFormat(imageFormat)) {
+ throw new IllegalArgumentException(
+ "Invalid imageFormat is specified: " + imageFormat);
+ }
+ mImageFormat = imageFormat;
+ mUseLegacyImageFormat = true;
+ mHardwareBufferFormat = HardwareBuffer.RGBA_8888;
+ mDataSpace = DataSpace.DATASPACE_UNKNOWN;
+ mUseSurfaceImageFormatInfo = false;
+ return this;
+ }
+
+ /**
+ * Set the hardwareBuffer format of this ImageWriter. The default value is
+ * {@link HardwareBuffer#RGBA_8888 HardwareBuffer.RGBA_8888}.
+ *
+ * <p>This function works together with {@link #setDataSpace} for an
+ * {@link ImageWriter} instance. Setting at least one of these two replaces
+ * {@link #setImageFormat} function.</p>
+ *
+ * @param hardwareBufferFormat The HardwareBuffer format of the image that this writer
+ * will produce.
+ * @return the Builder instance with customized buffer format.
+ *
+ * @see #setDataSpace
+ * @see #setImageFormat
+ */
+ public @NonNull Builder setHardwareBufferFormat(
+ @HardwareBuffer.Format int hardwareBufferFormat) {
+ mHardwareBufferFormat = hardwareBufferFormat;
+ mImageFormat = ImageFormat.UNKNOWN;
+ mUseLegacyImageFormat = false;
+ mUseSurfaceImageFormatInfo = false;
+ return this;
+ }
+
+ /**
+ * Set the dataspace of this ImageWriter.
+ * The default value is {@link DataSpace#DATASPACE_UNKNOWN}.
+ *
+ * @param dataSpace The dataspace of the image that this writer will produce.
+ * @return the builder instance with customized dataspace value.
+ *
+ * @see #setHardwareBufferFormat
+ */
+ public @NonNull Builder setDataSpace(@NamedDataSpace long dataSpace) {
+ mDataSpace = dataSpace;
+ mImageFormat = ImageFormat.UNKNOWN;
+ mUseLegacyImageFormat = false;
+ mUseSurfaceImageFormatInfo = false;
+ return this;
+ }
+
+ /**
+ * Set the usage flag of this ImageWriter.
+ * Default value is {@link HardwareBuffer#USAGE_CPU_WRITE_OFTEN}.
+ *
+ * @param usage The intended usage of the images produced by this ImageWriter.
+ * @return the Builder instance with customized usage flag.
+ *
+ * @see HardwareBuffer
+ */
+ public @NonNull Builder setUsage(@Usage long usage) {
+ mUsage = usage;
+ return this;
+ }
+
+ /**
+ * Builds a new ImageWriter object.
+ *
+ * @return The new ImageWriter object.
+ */
+ public @NonNull ImageWriter build() {
+ if (mUseLegacyImageFormat) {
+ return new ImageWriter(mSurface, mMaxImages, mUseSurfaceImageFormatInfo,
+ mImageFormat, mWidth, mHeight, mUsage);
+ } else {
+ return new ImageWriter(mSurface, mMaxImages, mUseSurfaceImageFormatInfo,
+ mHardwareBufferFormat, mDataSpace, mWidth, mHeight, mUsage);
+ }
+ }
+ }
+
private static class WriterSurfaceImage extends android.media.Image {
private ImageWriter mOwner;
// This field is used by native code, do not access or modify.
@@ -774,6 +1032,13 @@ public class ImageWriter implements AutoCloseable {
public WriterSurfaceImage(ImageWriter writer) {
mOwner = writer;
+ mWidth = writer.mWidth;
+ mHeight = writer.mHeight;
+
+ if (!writer.mUseLegacyImageFormat) {
+ mFormat = PublicFormatUtils.getPublicFormat(
+ writer.mHardwareBufferFormat, writer.mDataSpace);
+ }
}
@Override
@@ -969,8 +1234,9 @@ public class ImageWriter implements AutoCloseable {
}
// Native implemented ImageWriter methods.
- private synchronized native long nativeInit(Object weakSelf, Surface surface, int maxImgs,
- int format, int width, int height);
+ private synchronized native long nativeInit(Object weakSelf, Surface surface, int maxImages,
+ int width, int height, boolean useSurfaceImageFormatInfo, int hardwareBufferFormat,
+ long dataSpace, long usage);
private synchronized native void nativeClose(long nativeCtx);
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 3c152fb68c0a..e75df1d9b691 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -603,6 +603,18 @@ public final class MediaCodecInfo {
public static final String FEATURE_QpBounds = "qp-bounds";
/**
+ * <b>video encoder only</b>: codec supports exporting encoding statistics.
+ * Encoders with this feature can provide the App clients with the encoding statistics
+ * information about the frame.
+ * The scope of encoding statistics is controlled by
+ * {@link MediaFormat#KEY_VIDEO_ENCODING_STATISTICS_LEVEL}.
+ *
+ * @see MediaFormat#KEY_VIDEO_ENCODING_STATISTICS_LEVEL
+ */
+ @SuppressLint("AllUpper") // for consistency with other FEATURE_* constants
+ public static final String FEATURE_EncodingStatistics = "encoding-statistics";
+
+ /**
* Query codec feature capabilities.
* <p>
* These features are supported to be used by the codec. These
@@ -641,6 +653,7 @@ public final class MediaCodecInfo {
new Feature(FEATURE_MultipleFrames, (1 << 1), false),
new Feature(FEATURE_DynamicTimestamp, (1 << 2), false),
new Feature(FEATURE_QpBounds, (1 << 3), false),
+ new Feature(FEATURE_EncodingStatistics, (1 << 4), false),
// feature to exclude codec from REGULAR codec list
new Feature(FEATURE_SpecialCodec, (1 << 30), false, true),
};
diff --git a/media/java/android/media/MediaDescription.java b/media/java/android/media/MediaDescription.java
index 458562afb6ef..dece6bdbb35f 100644
--- a/media/java/android/media/MediaDescription.java
+++ b/media/java/android/media/MediaDescription.java
@@ -142,10 +142,10 @@ public class MediaDescription implements Parcelable {
mTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
mSubtitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
mDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
- mIcon = in.readParcelable(null);
- mIconUri = in.readParcelable(null);
+ mIcon = in.readParcelable(null, android.graphics.Bitmap.class);
+ mIconUri = in.readParcelable(null, android.net.Uri.class);
mExtras = in.readBundle();
- mMediaUri = in.readParcelable(null);
+ mMediaUri = in.readParcelable(null, android.net.Uri.class);
}
/**
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index 4891d74f868f..4956dbefa240 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -1176,6 +1176,76 @@ public final class MediaFormat {
public static final String KEY_VIDEO_QP_B_MIN = "video-qp-b-min";
/**
+ * A key describing the level of encoding statistics information emitted from video encoder.
+ *
+ * The associated value is an integer.
+ */
+ public static final String KEY_VIDEO_ENCODING_STATISTICS_LEVEL =
+ "video-encoding-statistics-level";
+
+ /**
+ * Encoding Statistics Level None.
+ * Encoder generates no information about Encoding statistics.
+ */
+ public static final int VIDEO_ENCODING_STATISTICS_LEVEL_NONE = 0;
+
+ /**
+ * Encoding Statistics Level 1.
+ * Encoder generates {@link MediaFormat#KEY_PICTURE_TYPE} and
+ * {@link MediaFormat#KEY_VIDEO_QP_AVERAGE} for each frame.
+ */
+ public static final int VIDEO_ENCODING_STATISTICS_LEVEL_1 = 1;
+
+ /** @hide */
+ @IntDef({
+ VIDEO_ENCODING_STATISTICS_LEVEL_NONE,
+ VIDEO_ENCODING_STATISTICS_LEVEL_1,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface VideoEncodingStatisticsLevel {}
+
+ /**
+ * A key describing the per-frame average block QP (Quantization Parameter).
+ * This is a part of a video 'Encoding Statistics' export feature.
+ * This value is emitted from video encoder for a video frame.
+ * The average value is rounded down (using floor()) to integer value.
+ *
+ * The associated value is an integer.
+ */
+ public static final String KEY_VIDEO_QP_AVERAGE = "video-qp-average";
+
+ /**
+ * A key describing the picture type of the encoded frame.
+ * This is a part of a video 'Encoding Statistics' export feature.
+ * This value is emitted from video encoder for a video frame.
+ *
+ * The associated value is an integer.
+ */
+ public static final String KEY_PICTURE_TYPE = "picture-type";
+
+ /** Picture Type is unknown. */
+ public static final int PICTURE_TYPE_UNKNOWN = 0;
+
+ /** Picture Type is I Frame. */
+ public static final int PICTURE_TYPE_I = 1;
+
+ /** Picture Type is P Frame. */
+ public static final int PICTURE_TYPE_P = 2;
+
+ /** Picture Type is B Frame. */
+ public static final int PICTURE_TYPE_B = 3;
+
+ /** @hide */
+ @IntDef({
+ PICTURE_TYPE_UNKNOWN,
+ PICTURE_TYPE_I,
+ PICTURE_TYPE_P,
+ PICTURE_TYPE_B,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface PictureType {}
+
+ /**
* A key describing the audio session ID of the AudioTrack associated
* to a tunneled video codec.
* The associated value is an integer.
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 77c1e55b08cb..d7857a01f7ea 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -365,6 +365,7 @@ public class MediaRecorder implements AudioRouting,
*/
public static final int VOICE_PERFORMANCE = 10;
+
/**
* Source for an echo canceller to capture the reference signal to be cancelled.
* <p>
@@ -408,6 +409,15 @@ public class MediaRecorder implements AudioRouting,
@SystemApi
@RequiresPermission(android.Manifest.permission.CAPTURE_AUDIO_HOTWORD)
public static final int HOTWORD = 1999;
+
+ /** Microphone audio source for ultrasound sound if available, behaves like
+ * {@link #DEFAULT} otherwise.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.ACCESS_ULTRASOUND)
+ public static final int ULTRASOUND = 2000;
+
}
/** @hide */
@@ -442,6 +452,7 @@ public class MediaRecorder implements AudioRouting,
AudioSource.ECHO_REFERENCE,
AudioSource.RADIO_TUNER,
AudioSource.HOTWORD,
+ AudioSource.ULTRASOUND,
})
@Retention(RetentionPolicy.SOURCE)
public @interface SystemSource {}
@@ -454,20 +465,20 @@ public class MediaRecorder implements AudioRouting,
*/
public static boolean isSystemOnlyAudioSource(int source) {
switch(source) {
- case AudioSource.DEFAULT:
- case AudioSource.MIC:
- case AudioSource.VOICE_UPLINK:
- case AudioSource.VOICE_DOWNLINK:
- case AudioSource.VOICE_CALL:
- case AudioSource.CAMCORDER:
- case AudioSource.VOICE_RECOGNITION:
- case AudioSource.VOICE_COMMUNICATION:
- //case REMOTE_SUBMIX: considered "system" as it requires system permissions
- case AudioSource.UNPROCESSED:
- case AudioSource.VOICE_PERFORMANCE:
- return false;
- default:
- return true;
+ case AudioSource.DEFAULT:
+ case AudioSource.MIC:
+ case AudioSource.VOICE_UPLINK:
+ case AudioSource.VOICE_DOWNLINK:
+ case AudioSource.VOICE_CALL:
+ case AudioSource.CAMCORDER:
+ case AudioSource.VOICE_RECOGNITION:
+ case AudioSource.VOICE_COMMUNICATION:
+ //case REMOTE_SUBMIX: considered "system" as it requires system permissions
+ case AudioSource.UNPROCESSED:
+ case AudioSource.VOICE_PERFORMANCE:
+ return false;
+ default:
+ return true;
}
}
@@ -491,6 +502,7 @@ public class MediaRecorder implements AudioRouting,
case AudioSource.ECHO_REFERENCE:
case AudioSource.RADIO_TUNER:
case AudioSource.HOTWORD:
+ case AudioSource.ULTRASOUND:
return true;
default:
return false;
@@ -500,38 +512,40 @@ public class MediaRecorder implements AudioRouting,
/** @hide */
public static final String toLogFriendlyAudioSource(int source) {
switch(source) {
- case AudioSource.DEFAULT:
- return "DEFAULT";
- case AudioSource.MIC:
- return "MIC";
- case AudioSource.VOICE_UPLINK:
- return "VOICE_UPLINK";
- case AudioSource.VOICE_DOWNLINK:
- return "VOICE_DOWNLINK";
- case AudioSource.VOICE_CALL:
- return "VOICE_CALL";
- case AudioSource.CAMCORDER:
- return "CAMCORDER";
- case AudioSource.VOICE_RECOGNITION:
- return "VOICE_RECOGNITION";
- case AudioSource.VOICE_COMMUNICATION:
- return "VOICE_COMMUNICATION";
- case AudioSource.REMOTE_SUBMIX:
- return "REMOTE_SUBMIX";
- case AudioSource.UNPROCESSED:
- return "UNPROCESSED";
- case AudioSource.ECHO_REFERENCE:
- return "ECHO_REFERENCE";
- case AudioSource.VOICE_PERFORMANCE:
- return "VOICE_PERFORMANCE";
- case AudioSource.RADIO_TUNER:
- return "RADIO_TUNER";
- case AudioSource.HOTWORD:
- return "HOTWORD";
- case AudioSource.AUDIO_SOURCE_INVALID:
- return "AUDIO_SOURCE_INVALID";
- default:
- return "unknown source " + source;
+ case AudioSource.DEFAULT:
+ return "DEFAULT";
+ case AudioSource.MIC:
+ return "MIC";
+ case AudioSource.VOICE_UPLINK:
+ return "VOICE_UPLINK";
+ case AudioSource.VOICE_DOWNLINK:
+ return "VOICE_DOWNLINK";
+ case AudioSource.VOICE_CALL:
+ return "VOICE_CALL";
+ case AudioSource.CAMCORDER:
+ return "CAMCORDER";
+ case AudioSource.VOICE_RECOGNITION:
+ return "VOICE_RECOGNITION";
+ case AudioSource.VOICE_COMMUNICATION:
+ return "VOICE_COMMUNICATION";
+ case AudioSource.REMOTE_SUBMIX:
+ return "REMOTE_SUBMIX";
+ case AudioSource.UNPROCESSED:
+ return "UNPROCESSED";
+ case AudioSource.ECHO_REFERENCE:
+ return "ECHO_REFERENCE";
+ case AudioSource.VOICE_PERFORMANCE:
+ return "VOICE_PERFORMANCE";
+ case AudioSource.RADIO_TUNER:
+ return "RADIO_TUNER";
+ case AudioSource.HOTWORD:
+ return "HOTWORD";
+ case AudioSource.ULTRASOUND:
+ return "ULTRASOUND";
+ case AudioSource.AUDIO_SOURCE_INVALID:
+ return "AUDIO_SOURCE_INVALID";
+ default:
+ return "unknown source " + source;
}
}
diff --git a/media/java/android/media/MediaRoute2Info.java b/media/java/android/media/MediaRoute2Info.java
index 9c9e83b0987d..2427fa64562d 100644
--- a/media/java/android/media/MediaRoute2Info.java
+++ b/media/java/android/media/MediaRoute2Info.java
@@ -371,7 +371,7 @@ public final class MediaRoute2Info implements Parcelable {
mFeatures = in.createStringArrayList();
mType = in.readInt();
mIsSystem = in.readBoolean();
- mIconUri = in.readParcelable(null);
+ mIconUri = in.readParcelable(null, android.net.Uri.class);
mDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
mConnectionState = in.readInt();
mClientPackageName = in.readString();
diff --git a/media/java/android/media/Ringtone.java b/media/java/android/media/Ringtone.java
index 3cf03417334b..86a94a9e0662 100644
--- a/media/java/android/media/Ringtone.java
+++ b/media/java/android/media/Ringtone.java
@@ -504,49 +504,51 @@ public class Ringtone {
}
private boolean playFallbackRingtone() {
- if (mAudioManager.getStreamVolume(AudioAttributes.toLegacyStreamType(mAudioAttributes))
- != 0) {
- int ringtoneType = RingtoneManager.getDefaultType(mUri);
- if (ringtoneType == -1 ||
- RingtoneManager.getActualDefaultRingtoneUri(mContext, ringtoneType) != null) {
- // Default ringtone, try fallback ringtone.
- try {
- AssetFileDescriptor afd = mContext.getResources().openRawResourceFd(
- com.android.internal.R.raw.fallbackring);
- if (afd != null) {
- mLocalPlayer = new MediaPlayer();
- if (afd.getDeclaredLength() < 0) {
- mLocalPlayer.setDataSource(afd.getFileDescriptor());
- } else {
- mLocalPlayer.setDataSource(afd.getFileDescriptor(),
- afd.getStartOffset(),
- afd.getDeclaredLength());
- }
- mLocalPlayer.setAudioAttributes(mAudioAttributes);
- synchronized (mPlaybackSettingsLock) {
- applyPlaybackProperties_sync();
- }
- if (mVolumeShaperConfig != null) {
- mVolumeShaper = mLocalPlayer.createVolumeShaper(mVolumeShaperConfig);
- }
- mLocalPlayer.prepare();
- startLocalPlayer();
- afd.close();
- return true;
- } else {
- Log.e(TAG, "Could not load fallback ringtone");
- }
- } catch (IOException ioe) {
- destroyLocalPlayer();
- Log.e(TAG, "Failed to open fallback ringtone");
- } catch (NotFoundException nfe) {
- Log.e(TAG, "Fallback ringtone does not exist");
- }
+ int streamType = AudioAttributes.toLegacyStreamType(mAudioAttributes);
+ if (mAudioManager.getStreamVolume(streamType) == 0) {
+ return false;
+ }
+ int ringtoneType = RingtoneManager.getDefaultType(mUri);
+ if (ringtoneType != -1 &&
+ RingtoneManager.getActualDefaultRingtoneUri(mContext, ringtoneType) == null) {
+ Log.w(TAG, "not playing fallback for " + mUri);
+ return false;
+ }
+ // Default ringtone, try fallback ringtone.
+ try {
+ AssetFileDescriptor afd = mContext.getResources().openRawResourceFd(
+ com.android.internal.R.raw.fallbackring);
+ if (afd == null) {
+ Log.e(TAG, "Could not load fallback ringtone");
+ return false;
+ }
+ mLocalPlayer = new MediaPlayer();
+ if (afd.getDeclaredLength() < 0) {
+ mLocalPlayer.setDataSource(afd.getFileDescriptor());
} else {
- Log.w(TAG, "not playing fallback for " + mUri);
+ mLocalPlayer.setDataSource(afd.getFileDescriptor(),
+ afd.getStartOffset(),
+ afd.getDeclaredLength());
}
+ mLocalPlayer.setAudioAttributes(mAudioAttributes);
+ synchronized (mPlaybackSettingsLock) {
+ applyPlaybackProperties_sync();
+ }
+ if (mVolumeShaperConfig != null) {
+ mVolumeShaper = mLocalPlayer.createVolumeShaper(mVolumeShaperConfig);
+ }
+ mLocalPlayer.prepare();
+ startLocalPlayer();
+ afd.close();
+ } catch (IOException ioe) {
+ destroyLocalPlayer();
+ Log.e(TAG, "Failed to open fallback ringtone");
+ return false;
+ } catch (NotFoundException nfe) {
+ Log.e(TAG, "Fallback ringtone does not exist");
+ return false;
}
- return false;
+ return true;
}
void setTitle(String title) {
diff --git a/media/java/android/media/midi/IMidiManager.aidl b/media/java/android/media/midi/IMidiManager.aidl
index d3c8e0adb3ce..b03f78504635 100644
--- a/media/java/android/media/midi/IMidiManager.aidl
+++ b/media/java/android/media/midi/IMidiManager.aidl
@@ -31,6 +31,8 @@ interface IMidiManager
{
MidiDeviceInfo[] getDevices();
+ MidiDeviceInfo[] getDevicesForTransport(int transport);
+
// for device creation & removal notifications
void registerListener(IBinder clientToken, in IMidiDeviceListener listener);
void unregisterListener(IBinder clientToken, in IMidiDeviceListener listener);
@@ -43,7 +45,7 @@ interface IMidiManager
// for registering built-in MIDI devices
MidiDeviceInfo registerDeviceServer(in IMidiDeviceServer server, int numInputPorts,
int numOutputPorts, in String[] inputPortNames, in String[] outputPortNames,
- in Bundle properties, int type);
+ in Bundle properties, int type, int defaultProtocol);
// for unregistering built-in MIDI devices
void unregisterDeviceServer(in IMidiDeviceServer server);
diff --git a/media/java/android/media/midi/MidiDeviceInfo.java b/media/java/android/media/midi/MidiDeviceInfo.java
index dd3b6dbd6a39..48c50f01c01c 100644
--- a/media/java/android/media/midi/MidiDeviceInfo.java
+++ b/media/java/android/media/midi/MidiDeviceInfo.java
@@ -16,11 +16,15 @@
package android.media.midi;
+import android.annotation.IntDef;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* This class contains information to describe a MIDI device.
* For now we only have information that can be retrieved easily for USB devices,
@@ -54,6 +58,110 @@ public final class MidiDeviceInfo implements Parcelable {
public static final int TYPE_BLUETOOTH = 3;
/**
+ * Constant representing a default protocol with Universal MIDI Packets (UMP).
+ * UMP is defined in "Universal MIDI Packet (UMP) Format and MIDI 2.0 Protocol" spec.
+ * All UMP data should be a multiple of 4 bytes.
+ * Use UMP to negotiate with the device with MIDI-CI.
+ * MIDI-CI is defined in "MIDI Capability Inquiry (MIDI-CI)" spec.
+ * Call {@link MidiManager#getDevicesForTransport} with parameter
+ * {@link MidiManager#TRANSPORT_UNIVERSAL_MIDI_PACKETS} to get devices with this transport.
+ * @see MidiDeviceInfo#getDefaultProtocol
+ */
+ public static final int PROTOCOL_UMP_USE_MIDI_CI = 0;
+
+ /**
+ * Constant representing a default protocol with Universal MIDI Packets (UMP).
+ * UMP is defined in "Universal MIDI Packet (UMP) Format and MIDI 2.0 Protocol" spec.
+ * All UMP data should be a multiple of 4 bytes.
+ * Use MIDI 1.0 through UMP with packet sizes up to 64 bits.
+ * Call {@link MidiManager#getDevicesForTransport} with parameter
+ * {@link MidiManager#TRANSPORT_UNIVERSAL_MIDI_PACKETS} to get devices with this transport.
+ * @see MidiDeviceInfo#getDefaultProtocol
+ */
+ public static final int PROTOCOL_UMP_MIDI_1_0_UP_TO_64_BITS = 1;
+
+ /**
+ * Constant representing a default protocol with Universal MIDI Packets (UMP).
+ * UMP is defined in "Universal MIDI Packet (UMP) Format and MIDI 2.0 Protocol" spec.
+ * All UMP data should be a multiple of 4 bytes.
+ * Use MIDI 1.0 through UMP with packet sizes up to 64 bits and jitter reduction timestamps.
+ * Call {@link MidiManager#getDevicesForTransport} with parameter
+ * {@link MidiManager#TRANSPORT_UNIVERSAL_MIDI_PACKETS} to get devices with this transport.
+ * @see MidiDeviceInfo#getDefaultProtocol
+ */
+ public static final int PROTOCOL_UMP_MIDI_1_0_UP_TO_64_BITS_AND_JRTS = 2;
+
+ /**
+ * Constant representing a default protocol with Universal MIDI Packets (UMP).
+ * UMP is defined in "Universal MIDI Packet (UMP) Format and MIDI 2.0 Protocol" spec.
+ * All UMP data should be a multiple of 4 bytes.
+ * Use MIDI 1.0 through UMP with packet sizes up to 128 bits.
+ * Call {@link MidiManager#getDevicesForTransport} with parameter
+ * {@link MidiManager#TRANSPORT_UNIVERSAL_MIDI_PACKETS} to get devices with this transport.
+ * @see MidiDeviceInfo#getDefaultProtocol
+ */
+ public static final int PROTOCOL_UMP_MIDI_1_0_UP_TO_128_BITS = 3;
+
+ /**
+ * Constant representing a default protocol with Universal MIDI Packets (UMP).
+ * UMP is defined in "Universal MIDI Packet (UMP) Format and MIDI 2.0 Protocol" spec.
+ * All UMP data should be a multiple of 4 bytes.
+ * Use MIDI 1.0 through UMP with packet sizes up to 128 bits and jitter reduction timestamps.
+ * Call {@link MidiManager#getDevicesForTransport} with parameter
+ * {@link MidiManager#TRANSPORT_UNIVERSAL_MIDI_PACKETS} to get devices with this transport.
+ * @see MidiDeviceInfo#getDefaultProtocol
+ */
+ public static final int PROTOCOL_UMP_MIDI_1_0_UP_TO_128_BITS_AND_JRTS = 4;
+
+ /**
+ * Constant representing a default protocol with Universal MIDI Packets (UMP).
+ * UMP is defined in "Universal MIDI Packet (UMP) Format and MIDI 2.0 Protocol" spec.
+ * All UMP data should be a multiple of 4 bytes.
+ * Use MIDI 2.0 through UMP.
+ * Call {@link MidiManager#getDevicesForTransport} with parameter
+ * {@link MidiManager#TRANSPORT_UNIVERSAL_MIDI_PACKETS} to get devices with this transport.
+ * @see MidiDeviceInfo#getDefaultProtocol
+ */
+ public static final int PROTOCOL_UMP_MIDI_2_0 = 17;
+
+ /**
+ * Constant representing a default protocol with Universal MIDI Packets (UMP).
+ * UMP is defined in "Universal MIDI Packet (UMP) Format and MIDI 2.0 Protocol" spec.
+ * All UMP data should be a multiple of 4 bytes.
+ * Use MIDI 2.0 through UMP and jitter reduction timestamps.
+ * Call {@link MidiManager#getDevicesForTransport} with parameter
+ * {@link MidiManager#TRANSPORT_UNIVERSAL_MIDI_PACKETS} to get devices with this transport.
+ * @see MidiDeviceInfo#getDefaultProtocol
+ */
+ public static final int PROTOCOL_UMP_MIDI_2_0_AND_JRTS = 18;
+
+ /**
+ * Constant representing a device with an unknown default protocol.
+ * If Universal MIDI Packets (UMP) are needed, use MIDI-CI through MIDI 1.0.
+ * UMP is defined in "Universal MIDI Packet (UMP) Format and MIDI 2.0 Protocol" spec.
+ * MIDI-CI is defined in "MIDI Capability Inquiry (MIDI-CI)" spec.
+ * @see MidiDeviceInfo#getDefaultProtocol
+ */
+ public static final int PROTOCOL_UNKNOWN = -1;
+
+ /**
+ * @see MidiDeviceInfo#getDefaultProtocol
+ * @hide
+ */
+ @IntDef(prefix = { "PROTOCOL_" }, value = {
+ PROTOCOL_UMP_USE_MIDI_CI,
+ PROTOCOL_UMP_MIDI_1_0_UP_TO_64_BITS,
+ PROTOCOL_UMP_MIDI_1_0_UP_TO_64_BITS_AND_JRTS,
+ PROTOCOL_UMP_MIDI_1_0_UP_TO_128_BITS,
+ PROTOCOL_UMP_MIDI_1_0_UP_TO_128_BITS_AND_JRTS,
+ PROTOCOL_UMP_MIDI_2_0,
+ PROTOCOL_UMP_MIDI_2_0_AND_JRTS,
+ PROTOCOL_UNKNOWN
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Protocol {}
+
+ /**
* Bundle key for the device's user visible name property.
* The value for this property is of type {@link java.lang.String}.
* Used with the {@link android.os.Bundle} returned by {@link #getProperties}.
@@ -196,6 +304,7 @@ public final class MidiDeviceInfo implements Parcelable {
private final String[] mOutputPortNames;
private final Bundle mProperties;
private final boolean mIsPrivate;
+ private final int mDefaultProtocol;
/**
* MidiDeviceInfo should only be instantiated by MidiService implementation
@@ -203,7 +312,7 @@ public final class MidiDeviceInfo implements Parcelable {
*/
public MidiDeviceInfo(int type, int id, int numInputPorts, int numOutputPorts,
String[] inputPortNames, String[] outputPortNames, Bundle properties,
- boolean isPrivate) {
+ boolean isPrivate, int defaultProtocol) {
// Check num ports for out-of-range values. Typical values will be
// between zero and three. More than 16 would be very unlikely
// because the port index field in the USB packet is only 4 bits.
@@ -234,6 +343,7 @@ public final class MidiDeviceInfo implements Parcelable {
}
mProperties = properties;
mIsPrivate = isPrivate;
+ mDefaultProtocol = defaultProtocol;
}
/**
@@ -312,6 +422,18 @@ public final class MidiDeviceInfo implements Parcelable {
return mIsPrivate;
}
+ /**
+ * Returns the default protocol. For most devices, this will be {@link #PROTOCOL_UNKNOWN}.
+ * Returning {@link #PROTOCOL_UNKNOWN} is not an error; the device just doesn't support
+ * Universal MIDI Packets by default.
+ *
+ * @return the device's default protocol.
+ */
+ @Protocol
+ public int getDefaultProtocol() {
+ return mDefaultProtocol;
+ }
+
@Override
public boolean equals(Object o) {
if (o instanceof MidiDeviceInfo) {
@@ -331,11 +453,12 @@ public final class MidiDeviceInfo implements Parcelable {
// This is a hack to force the mProperties Bundle to unparcel so we can
// print all the names and values.
mProperties.getString(PROPERTY_NAME);
- return ("MidiDeviceInfo[mType=" + mType +
- ",mInputPortCount=" + mInputPortCount +
- ",mOutputPortCount=" + mOutputPortCount +
- ",mProperties=" + mProperties +
- ",mIsPrivate=" + mIsPrivate);
+ return ("MidiDeviceInfo[mType=" + mType
+ + ",mInputPortCount=" + mInputPortCount
+ + ",mOutputPortCount=" + mOutputPortCount
+ + ",mProperties=" + mProperties
+ + ",mIsPrivate=" + mIsPrivate
+ + ",mDefaultProtocol=" + mDefaultProtocol);
}
public static final @android.annotation.NonNull Parcelable.Creator<MidiDeviceInfo> CREATOR =
@@ -349,10 +472,12 @@ public final class MidiDeviceInfo implements Parcelable {
String[] inputPortNames = in.createStringArray();
String[] outputPortNames = in.createStringArray();
boolean isPrivate = (in.readInt() == 1);
+ int defaultProtocol = in.readInt();
Bundle basicPropertiesIgnored = in.readBundle();
Bundle properties = in.readBundle();
return new MidiDeviceInfo(type, id, inputPortCount, outputPortCount,
- inputPortNames, outputPortNames, properties, isPrivate);
+ inputPortNames, outputPortNames, properties, isPrivate,
+ defaultProtocol);
}
public MidiDeviceInfo[] newArray(int size) {
@@ -390,6 +515,7 @@ public final class MidiDeviceInfo implements Parcelable {
parcel.writeStringArray(mInputPortNames);
parcel.writeStringArray(mOutputPortNames);
parcel.writeInt(mIsPrivate ? 1 : 0);
+ parcel.writeInt(mDefaultProtocol);
// "Basic" properties only contain properties of primitive types
// and thus can be read back by native code. "Extra" properties is
// a superset that contains all properties.
diff --git a/media/java/android/media/midi/MidiDeviceStatus.java b/media/java/android/media/midi/MidiDeviceStatus.java
index b11827966b1a..aa0626742ac2 100644
--- a/media/java/android/media/midi/MidiDeviceStatus.java
+++ b/media/java/android/media/midi/MidiDeviceStatus.java
@@ -115,7 +115,7 @@ public final class MidiDeviceStatus implements Parcelable {
new Parcelable.Creator<MidiDeviceStatus>() {
public MidiDeviceStatus createFromParcel(Parcel in) {
ClassLoader classLoader = MidiDeviceInfo.class.getClassLoader();
- MidiDeviceInfo deviceInfo = in.readParcelable(classLoader);
+ MidiDeviceInfo deviceInfo = in.readParcelable(classLoader, android.media.midi.MidiDeviceInfo.class);
boolean[] inputPortOpen = in.createBooleanArray();
int[] outputPortOpenCount = in.createIntArray();
return new MidiDeviceStatus(deviceInfo, inputPortOpen, outputPortOpenCount);
diff --git a/media/java/android/media/midi/MidiManager.java b/media/java/android/media/midi/MidiManager.java
index dee94c681e87..5348d4e358d0 100644
--- a/media/java/android/media/midi/MidiManager.java
+++ b/media/java/android/media/midi/MidiManager.java
@@ -16,19 +16,31 @@
package android.media.midi;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.RequiresFeature;
import android.annotation.SystemService;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Binder;
-import android.os.IBinder;
import android.os.Bundle;
import android.os.Handler;
+import android.os.IBinder;
import android.os.RemoteException;
+import android.util.ArraySet;
import android.util.Log;
+import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Collections;
+import java.util.Objects;
+import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executor;
+
+// BLE-MIDI
/**
* This class is the public application interface to the MIDI service.
@@ -39,6 +51,39 @@ public final class MidiManager {
private static final String TAG = "MidiManager";
/**
+ * Constant representing MIDI devices.
+ * These devices do NOT support Universal MIDI Packets by default.
+ * These support the original MIDI 1.0 byte stream.
+ * When communicating to a USB device, a raw byte stream will be padded for USB.
+ * Likewise, for a Bluetooth device, the raw bytes will be converted for Bluetooth.
+ * For virtual devices, the byte stream will be passed directly.
+ * If Universal MIDI Packets are needed, please use MIDI-CI.
+ * @see MidiManager#getDevicesForTransport
+ */
+ public static final int TRANSPORT_MIDI_BYTE_STREAM = 1;
+
+ /**
+ * Constant representing Universal MIDI devices.
+ * These devices do support Universal MIDI Packets (UMP) by default.
+ * When sending data to these devices, please send UMP.
+ * Packets should always be a multiple of 4 bytes.
+ * UMP is defined in the USB MIDI 2.0 spec. Please read the standard for more info.
+ * @see MidiManager#getDevicesForTransport
+ */
+ public static final int TRANSPORT_UNIVERSAL_MIDI_PACKETS = 2;
+
+ /**
+ * @see MidiManager#getDevicesForTransport
+ * @hide
+ */
+ @IntDef(prefix = { "TRANSPORT_" }, value = {
+ TRANSPORT_MIDI_BYTE_STREAM,
+ TRANSPORT_UNIVERSAL_MIDI_PACKETS
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Transport {}
+
+ /**
* Intent for starting BluetoothMidiService
* @hide
*/
@@ -68,43 +113,67 @@ public final class MidiManager {
private class DeviceListener extends IMidiDeviceListener.Stub {
private final DeviceCallback mCallback;
private final Handler mHandler;
+ private final Executor mExecutor;
+ private final int mTransport;
- public DeviceListener(DeviceCallback callback, Handler handler) {
+ DeviceListener(DeviceCallback callback, Handler handler, int transport) {
mCallback = callback;
mHandler = handler;
+ mExecutor = null;
+ mTransport = transport;
+ }
+
+ DeviceListener(DeviceCallback callback, Executor executor, int transport) {
+ mCallback = callback;
+ mHandler = null;
+ mExecutor = executor;
+ mTransport = transport;
}
@Override
public void onDeviceAdded(MidiDeviceInfo device) {
- if (mHandler != null) {
- final MidiDeviceInfo deviceF = device;
- mHandler.post(new Runnable() {
- @Override public void run() {
- mCallback.onDeviceAdded(deviceF);
- }
- });
- } else {
- mCallback.onDeviceAdded(device);
+ if (shouldInvokeCallback(device)) {
+ if (mExecutor != null) {
+ mExecutor.execute(() ->
+ mCallback.onDeviceAdded(device));
+ } else if (mHandler != null) {
+ final MidiDeviceInfo deviceF = device;
+ mHandler.post(new Runnable() {
+ @Override public void run() {
+ mCallback.onDeviceAdded(deviceF);
+ }
+ });
+ } else {
+ mCallback.onDeviceAdded(device);
+ }
}
}
@Override
public void onDeviceRemoved(MidiDeviceInfo device) {
- if (mHandler != null) {
- final MidiDeviceInfo deviceF = device;
- mHandler.post(new Runnable() {
- @Override public void run() {
- mCallback.onDeviceRemoved(deviceF);
- }
- });
- } else {
- mCallback.onDeviceRemoved(device);
+ if (shouldInvokeCallback(device)) {
+ if (mExecutor != null) {
+ mExecutor.execute(() ->
+ mCallback.onDeviceRemoved(device));
+ } else if (mHandler != null) {
+ final MidiDeviceInfo deviceF = device;
+ mHandler.post(new Runnable() {
+ @Override public void run() {
+ mCallback.onDeviceRemoved(deviceF);
+ }
+ });
+ } else {
+ mCallback.onDeviceRemoved(device);
+ }
}
}
@Override
public void onDeviceStatusChanged(MidiDeviceStatus status) {
- if (mHandler != null) {
+ if (mExecutor != null) {
+ mExecutor.execute(() ->
+ mCallback.onDeviceStatusChanged(status));
+ } else if (mHandler != null) {
final MidiDeviceStatus statusF = status;
mHandler.post(new Runnable() {
@Override public void run() {
@@ -115,6 +184,25 @@ public final class MidiManager {
mCallback.onDeviceStatusChanged(status);
}
}
+
+ /**
+ * Used to figure out whether callbacks should be invoked. Only invoke callbacks of
+ * the correct type.
+ *
+ * @param MidiDeviceInfo the device to check
+ * @return whether to invoke a callback
+ */
+ private boolean shouldInvokeCallback(MidiDeviceInfo device) {
+ // UMP devices have protocols that are not PROTOCOL_UNKNOWN
+ if (mTransport == TRANSPORT_UNIVERSAL_MIDI_PACKETS) {
+ return (device.getDefaultProtocol() != MidiDeviceInfo.PROTOCOL_UNKNOWN);
+ } else if (mTransport == TRANSPORT_MIDI_BYTE_STREAM) {
+ return (device.getDefaultProtocol() == MidiDeviceInfo.PROTOCOL_UNKNOWN);
+ } else {
+ Log.e(TAG, "Invalid transport type: " + mTransport);
+ return false;
+ }
+ }
}
/**
@@ -167,8 +255,10 @@ public final class MidiManager {
}
/**
- * Registers a callback to receive notifications when MIDI devices are added and removed.
- *
+ * Registers a callback to receive notifications when MIDI 1.0 devices are added and removed.
+ * These are devices that do not default to Universal MIDI Packets. To register for a callback
+ * for those, call {@link #registerDeviceCallback} instead.
+
* The {@link DeviceCallback#onDeviceStatusChanged} method will be called immediately
* for any devices that have open ports. This allows applications to know which input
* ports are already in use and, therefore, unavailable.
@@ -180,9 +270,42 @@ public final class MidiManager {
* @param handler The {@link android.os.Handler Handler} that will be used for delivering the
* device notifications. If handler is null, then the thread used for the
* callback is unspecified.
+ * @deprecated Use the {@link #registerDeviceCallback}
+ * method with Executor and transport instead.
*/
+ @Deprecated
public void registerDeviceCallback(DeviceCallback callback, Handler handler) {
- DeviceListener deviceListener = new DeviceListener(callback, handler);
+ DeviceListener deviceListener = new DeviceListener(callback, handler,
+ TRANSPORT_MIDI_BYTE_STREAM);
+ try {
+ mService.registerListener(mToken, deviceListener);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ mDeviceListeners.put(callback, deviceListener);
+ }
+
+ /**
+ * Registers a callback to receive notifications when MIDI devices are added and removed
+ * for a specific transport type.
+ *
+ * The {@link DeviceCallback#onDeviceStatusChanged} method will be called immediately
+ * for any devices that have open ports. This allows applications to know which input
+ * ports are already in use and, therefore, unavailable.
+ *
+ * Applications should call {@link #getDevicesForTransport} before registering the callback
+ * to get a list of devices already added.
+ *
+ * @param transport The transport to be used. This is either TRANSPORT_MIDI_BYTE_STREAM or
+ * TRANSPORT_UNIVERSAL_MIDI_PACKETS.
+ * @param executor The {@link Executor} that will be used for delivering the
+ * device notifications.
+ * @param callback a {@link DeviceCallback} for MIDI device notifications
+ */
+ public void registerDeviceCallback(@Transport int transport,
+ @NonNull Executor executor, @NonNull DeviceCallback callback) {
+ Objects.requireNonNull(executor);
+ DeviceListener deviceListener = new DeviceListener(callback, executor, transport);
try {
mService.registerListener(mToken, deviceListener);
} catch (RemoteException e) {
@@ -208,10 +331,14 @@ public final class MidiManager {
}
/**
- * Gets the list of all connected MIDI devices.
+ * Gets a list of connected MIDI devices. This returns all devices that do
+ * not default to Universal MIDI Packets. To get those instead, please call
+ * {@link #getDevicesForTransport} instead.
*
- * @return an array of all MIDI devices
+ * @return an array of MIDI devices
+ * @deprecated Use {@link #getDevicesForTransport} instead.
*/
+ @Deprecated
public MidiDeviceInfo[] getDevices() {
try {
return mService.getDevices();
@@ -220,6 +347,28 @@ public final class MidiManager {
}
}
+ /**
+ * Gets a list of connected MIDI devices by transport. TRANSPORT_MIDI_BYTE_STREAM
+ * is used for MIDI 1.0 and is the most common.
+ * For devices with built in Universal MIDI Packet support, use
+ * TRANSPORT_UNIVERSAL_MIDI_PACKETS instead.
+ *
+ * @param transport The transport to be used. This is either TRANSPORT_MIDI_BYTE_STREAM or
+ * TRANSPORT_UNIVERSAL_MIDI_PACKETS.
+ * @return a collection of MIDI devices
+ */
+ public @NonNull Set<MidiDeviceInfo> getDevicesForTransport(@Transport int transport) {
+ try {
+ MidiDeviceInfo[] devices = mService.getDevicesForTransport(transport);
+ if (devices == null) {
+ return Collections.emptySet();
+ }
+ return new ArraySet<>(devices);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
private void sendOpenDeviceResponse(final MidiDevice device,
final OnDeviceOpenedListener listener, Handler handler) {
if (handler != null) {
@@ -284,9 +433,11 @@ public final class MidiManager {
final OnDeviceOpenedListener listenerF = listener;
final Handler handlerF = handler;
+ Log.d(TAG, "openBluetoothDevice() " + bluetoothDevice);
IMidiDeviceOpenCallback callback = new IMidiDeviceOpenCallback.Stub() {
@Override
public void onDeviceOpened(IMidiDeviceServer server, IBinder deviceToken) {
+ Log.d(TAG, "onDeviceOpened() server:" + server);
MidiDevice device = null;
if (server != null) {
try {
@@ -308,16 +459,26 @@ public final class MidiManager {
}
}
+ /** @hide */ // for now
+ public void closeBluetoothDevice(@NonNull MidiDevice midiDevice) {
+ try {
+ midiDevice.close();
+ } catch (IOException ex) {
+ Log.e(TAG, "Exception closing BLE-MIDI device" + ex);
+ }
+ }
+
/** @hide */
public MidiDeviceServer createDeviceServer(MidiReceiver[] inputPortReceivers,
int numOutputPorts, String[] inputPortNames, String[] outputPortNames,
- Bundle properties, int type, MidiDeviceServer.Callback callback) {
+ Bundle properties, int type, int defaultProtocol,
+ MidiDeviceServer.Callback callback) {
try {
MidiDeviceServer server = new MidiDeviceServer(mService, inputPortReceivers,
numOutputPorts, callback);
MidiDeviceInfo deviceInfo = mService.registerDeviceServer(server.getBinderInterface(),
inputPortReceivers.length, numOutputPorts, inputPortNames, outputPortNames,
- properties, type);
+ properties, type, defaultProtocol);
if (deviceInfo == null) {
Log.e(TAG, "registerVirtualDevice failed");
return null;
diff --git a/media/java/android/media/midi/package.html b/media/java/android/media/midi/package.html
index 33c54900cd07..67df1b2fa315 100644
--- a/media/java/android/media/midi/package.html
+++ b/media/java/android/media/midi/package.html
@@ -405,5 +405,46 @@ apps using the
<a href="#get_list_of_already_plugged_in_entities">MIDI device discovery calls described above</a>.
</p>
+<h1 id=using_midi_2_0_over_usb>Using MIDI 2.0 over USB</h1>
+
+<p>An app can use MIDI 2.0 over USB starting in Android T. MIDI 2.0 packets are embedded in
+Universal MIDI Packets, or UMP for short. A MIDI 2.0 USB device should create two interfaces,
+one endpoint that accepts only MIDI 1.0 packets and one that accepts only UMP packets.
+For more info about MIDI 2.0 and UMP, please read the MIDI 2.0 USB spec.</p>
+
+<p>MidiManager.getDevices() would simply return the 1.0 interface. This interface should work
+exactly the same as before. In order to use the new UMP interface, retrieve the device with the
+following code snippet.</p>
+
+<pre class=prettyprint>
+Collection&#60;MidiDeviceInfo&#62; universalDeviceInfos = midiManager.getDevicesForTransport(
+ MidiManager.TRANSPORT_UNIVERSAL_MIDI_PACKETS);
+</pre>
+
+<p>UMP Packets are always in multiple of 4 bytes. For each set of 4 bytes, they are sent in network
+order. Compare the following NoteOn code snippet with the NoteOn code snippet above. </p>
+
+<pre class=prettyprint>
+byte[] buffer = new byte[32];
+int numBytes = 0;
+int channel = 3; // MIDI channels 1-16 are encoded as 0-15.
+int group = 0;
+buffer[numBytes++] = (byte)(0x20 + group); // MIDI 1.0 voice message
+buffer[numBytes++] = (byte)(0x90 + (channel - 1)); // note on
+buffer[numBytes++] = (byte)60; // pitch is middle C
+buffer[numBytes++] = (byte)127; // max velocity
+int offset = 0;
+// post is non-blocking
+inputPort.send(buffer, offset, numBytes);
+</pre>
+
+<p>MIDI 2.0 messages can be sent through UMP after negotiating with the device. This is called
+MIDI-CI and is documented in the MIDI 2.0 spec. Some USB devices support pre-negotiated MIDI 2.0.
+For a MidiDeviceInfo, you can query the defaultProtocol.</p>
+
+<pre class=prettyprint>
+int defaultProtocol = info.getDefaultProtocol();
+</pre>
+
</body>
</html>
diff --git a/media/java/android/media/musicrecognition/RecognitionRequest.java b/media/java/android/media/musicrecognition/RecognitionRequest.java
index 3298d634d342..b8757a351e24 100644
--- a/media/java/android/media/musicrecognition/RecognitionRequest.java
+++ b/media/java/android/media/musicrecognition/RecognitionRequest.java
@@ -152,8 +152,8 @@ public final class RecognitionRequest implements Parcelable {
}
private RecognitionRequest(Parcel in) {
- mAudioFormat = in.readParcelable(AudioFormat.class.getClassLoader());
- mAudioAttributes = in.readParcelable(AudioAttributes.class.getClassLoader());
+ mAudioFormat = in.readParcelable(AudioFormat.class.getClassLoader(), android.media.AudioFormat.class);
+ mAudioAttributes = in.readParcelable(AudioAttributes.class.getClassLoader(), android.media.AudioAttributes.class);
mCaptureSession = in.readInt();
mMaxAudioLengthSeconds = in.readInt();
mIgnoreBeginningFrames = in.readInt();
diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java
index 1da41fb87b40..955ae3ca28fb 100644
--- a/media/java/android/media/session/MediaController.java
+++ b/media/java/android/media/session/MediaController.java
@@ -1022,7 +1022,7 @@ public final class MediaController {
mVolumeControl = in.readInt();
mMaxVolume = in.readInt();
mCurrentVolume = in.readInt();
- mAudioAttrs = in.readParcelable(null);
+ mAudioAttrs = in.readParcelable(null, android.media.AudioAttributes.class);
mVolumeControlId = in.readString();
}
diff --git a/media/java/android/media/tv/AitInfo.java b/media/java/android/media/tv/AitInfo.java
index ff4c6253b599..71b1634cef92 100644
--- a/media/java/android/media/tv/AitInfo.java
+++ b/media/java/android/media/tv/AitInfo.java
@@ -17,11 +17,12 @@
package android.media.tv;
import android.annotation.NonNull;
+import android.media.tv.interactive.TvInteractiveAppInfo;
import android.os.Parcel;
import android.os.Parcelable;
/**
- * AIT info.
+ * AIT (Application Information Table) info.
* @hide
*/
public final class AitInfo implements Parcelable {
@@ -50,14 +51,15 @@ public final class AitInfo implements Parcelable {
/**
* Constructs AIT info.
*/
- public AitInfo(int type, int version) {
+ public AitInfo(@TvInteractiveAppInfo.InteractiveAppType int type, int version) {
mType = type;
mVersion = version;
}
/**
- * Gets type.
+ * Gets interactive app type.
*/
+ @TvInteractiveAppInfo.InteractiveAppType
public int getType() {
return mType;
}
diff --git a/media/java/android/media/tv/DsmccResponse.java b/media/java/android/media/tv/DsmccResponse.java
index 4d496207051a..3ca63e323bcd 100644
--- a/media/java/android/media/tv/DsmccResponse.java
+++ b/media/java/android/media/tv/DsmccResponse.java
@@ -17,10 +17,13 @@
package android.media.tv;
import android.annotation.NonNull;
+import android.annotation.StringDef;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
@@ -29,6 +32,27 @@ public final class DsmccResponse extends BroadcastInfoResponse implements Parcel
public static final @TvInputManager.BroadcastInfoType int responseType =
TvInputManager.BROADCAST_INFO_TYPE_DSMCC;
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @StringDef(prefix = "BIOP_MESSAGE_TYPE_", value = {
+ BIOP_MESSAGE_TYPE_DIRECTORY,
+ BIOP_MESSAGE_TYPE_FILE,
+ BIOP_MESSAGE_TYPE_STREAM,
+ BIOP_MESSAGE_TYPE_SERVICE_GATEWAY,
+
+ })
+ public @interface BiopMessageType {}
+
+ /** Broadcast Inter-ORB Protocol (BIOP) message types */
+ /** BIOP directory message */
+ public static final String BIOP_MESSAGE_TYPE_DIRECTORY = "directory";
+ /** BIOP file message */
+ public static final String BIOP_MESSAGE_TYPE_FILE = "file";
+ /** BIOP stream message */
+ public static final String BIOP_MESSAGE_TYPE_STREAM = "stream";
+ /** BIOP service gateway message */
+ public static final String BIOP_MESSAGE_TYPE_SERVICE_GATEWAY = "service_gateway";
+
public static final @NonNull Parcelable.Creator<DsmccResponse> CREATOR =
new Parcelable.Creator<DsmccResponse>() {
@Override
@@ -43,39 +67,173 @@ public final class DsmccResponse extends BroadcastInfoResponse implements Parcel
}
};
+ private final @BiopMessageType String mBiopMessageType;
private final ParcelFileDescriptor mFileDescriptor;
- private final boolean mIsDirectory;
- private final List<String> mChildren;
+ private final List<String> mChildList;
+ private final int[] mEventIds;
+ private final String[] mEventNames;
public static DsmccResponse createFromParcelBody(Parcel in) {
return new DsmccResponse(in);
}
+ /**
+ * Constructs a BIOP file message response.
+ */
public DsmccResponse(int requestId, int sequence, @ResponseResult int responseResult,
- ParcelFileDescriptor file, boolean isDirectory, List<String> children) {
+ @NonNull ParcelFileDescriptor file) {
super(responseType, requestId, sequence, responseResult);
+ mBiopMessageType = BIOP_MESSAGE_TYPE_FILE;
mFileDescriptor = file;
- mIsDirectory = isDirectory;
- mChildren = children;
+ mChildList = null;
+ mEventIds = null;
+ mEventNames = null;
+ }
+
+ /**
+ * Constructs a BIOP service gateway or directory message response.
+ */
+ public DsmccResponse(int requestId, int sequence, @ResponseResult int responseResult,
+ boolean isServiceGateway, @NonNull List<String> childList) {
+ super(responseType, requestId, sequence, responseResult);
+ if (isServiceGateway) {
+ mBiopMessageType = BIOP_MESSAGE_TYPE_SERVICE_GATEWAY;
+ } else {
+ mBiopMessageType = BIOP_MESSAGE_TYPE_DIRECTORY;
+ }
+ mFileDescriptor = null;
+ mChildList = childList;
+ mEventIds = null;
+ mEventNames = null;
+ }
+
+ /**
+ * Constructs a BIOP stream message response.
+ *
+ * <p>The current stream message response does not support other stream messages types than
+ * stream event message type.
+ */
+ public DsmccResponse(int requestId, int sequence, @ResponseResult int responseResult,
+ @NonNull int[] eventIds, @NonNull String[] eventNames) {
+ super(responseType, requestId, sequence, responseResult);
+ mBiopMessageType = BIOP_MESSAGE_TYPE_STREAM;
+ mFileDescriptor = null;
+ mChildList = null;
+ mEventIds = eventIds;
+ mEventNames = eventNames;
+ if (mEventIds.length != eventNames.length) {
+ throw new IllegalStateException("The size of eventIds and eventNames must be equal");
+ }
}
- protected DsmccResponse(Parcel source) {
+ private DsmccResponse(@NonNull Parcel source) {
super(responseType, source);
- mFileDescriptor = source.readFileDescriptor();
- mIsDirectory = (source.readInt() == 1);
- mChildren = new ArrayList<>();
- source.readStringList(mChildren);
+
+ mBiopMessageType = source.readString();
+ switch (mBiopMessageType) {
+ case BIOP_MESSAGE_TYPE_SERVICE_GATEWAY:
+ case BIOP_MESSAGE_TYPE_DIRECTORY:
+ int childNum = source.readInt();
+ mChildList = new ArrayList<>();
+ for (int i = 0; i < childNum; i++) {
+ mChildList.add(source.readString());
+ }
+ mFileDescriptor = null;
+ mEventIds = null;
+ mEventNames = null;
+ break;
+ case BIOP_MESSAGE_TYPE_FILE:
+ mFileDescriptor = source.readFileDescriptor();
+ mChildList = null;
+ mEventIds = null;
+ mEventNames = null;
+ break;
+ case BIOP_MESSAGE_TYPE_STREAM:
+ int eventNum = source.readInt();
+ mEventIds = new int[eventNum];
+ mEventNames = new String[eventNum];
+ for (int i = 0; i < eventNum; i++) {
+ mEventIds[i] = source.readInt();
+ mEventNames[i] = source.readString();
+ }
+ mChildList = null;
+ mFileDescriptor = null;
+ break;
+ default:
+ throw new IllegalStateException("unexpected BIOP message type");
+ }
+ }
+
+ /** Returns the BIOP message type */
+ @NonNull
+ public @BiopMessageType String getBiopMessageType() {
+ return mBiopMessageType;
}
+ /** Returns the file descriptor for a given file message response */
+ @NonNull
public ParcelFileDescriptor getFile() {
+ if (!mBiopMessageType.equals(BIOP_MESSAGE_TYPE_FILE)) {
+ throw new IllegalStateException("Not file object");
+ }
return mFileDescriptor;
}
+ /**
+ * Returns a list of subobject names for the given service gateway or directory message
+ * response.
+ */
+ @NonNull
+ public List<String> getChildList() {
+ if (!mBiopMessageType.equals(BIOP_MESSAGE_TYPE_DIRECTORY)
+ && !mBiopMessageType.equals(BIOP_MESSAGE_TYPE_SERVICE_GATEWAY)) {
+ throw new IllegalStateException("Not directory object");
+ }
+ return new ArrayList<String>(mChildList);
+ }
+
+ /** Returns all event IDs carried in a given stream message response. */
+ @NonNull
+ public int[] getStreamEventIds() {
+ if (!mBiopMessageType.equals(BIOP_MESSAGE_TYPE_STREAM)) {
+ throw new IllegalStateException("Not stream event object");
+ }
+ return mEventIds;
+ }
+
+ /** Returns all event names carried in a given stream message response */
+ @NonNull
+ public String[] getStreamEventNames() {
+ if (!mBiopMessageType.equals(BIOP_MESSAGE_TYPE_STREAM)) {
+ throw new IllegalStateException("Not stream event object");
+ }
+ return mEventNames;
+ }
+
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
super.writeToParcel(dest, flags);
- mFileDescriptor.writeToParcel(dest, flags);
- dest.writeInt(mIsDirectory ? 1 : 0);
- dest.writeStringList(mChildren);
+ dest.writeString(mBiopMessageType);
+ switch (mBiopMessageType) {
+ case BIOP_MESSAGE_TYPE_SERVICE_GATEWAY:
+ case BIOP_MESSAGE_TYPE_DIRECTORY:
+ dest.writeInt(mChildList.size());
+ for (String child : mChildList) {
+ dest.writeString(child);
+ }
+ break;
+ case BIOP_MESSAGE_TYPE_FILE:
+ dest.writeFileDescriptor(mFileDescriptor.getFileDescriptor());
+ break;
+ case BIOP_MESSAGE_TYPE_STREAM:
+ dest.writeInt(mEventIds.length);
+ for (int i = 0; i < mEventIds.length; i++) {
+ dest.writeInt(mEventIds[i]);
+ dest.writeString(mEventNames[i]);
+ }
+ break;
+ default:
+ throw new IllegalStateException("unexpected BIOP message type");
+ }
}
}
diff --git a/media/java/android/media/tv/StreamEventResponse.java b/media/java/android/media/tv/StreamEventResponse.java
index fd7580107c71..903fab5c2268 100644
--- a/media/java/android/media/tv/StreamEventResponse.java
+++ b/media/java/android/media/tv/StreamEventResponse.java
@@ -39,54 +39,53 @@ public final class StreamEventResponse extends BroadcastInfoResponse implements
}
};
- private final String mName;
- private final String mText;
- private final String mData;
- private final String mStatus;
+ private final int mEventId;
+ private final long mNpt;
+ private final byte[] mData;
public static StreamEventResponse createFromParcelBody(Parcel in) {
return new StreamEventResponse(in);
}
public StreamEventResponse(int requestId, int sequence, @ResponseResult int responseResult,
- String name, String text, String data, String status) {
+ int eventId, long npt, @NonNull byte[] data) {
super(responseType, requestId, sequence, responseResult);
- mName = name;
- mText = text;
+ mEventId = eventId;
+ mNpt = npt;
mData = data;
- mStatus = status;
}
- protected StreamEventResponse(Parcel source) {
+ private StreamEventResponse(@NonNull Parcel source) {
super(responseType, source);
- mName = source.readString();
- mText = source.readString();
- mData = source.readString();
- mStatus = source.readString();
+ mEventId = source.readInt();
+ mNpt = source.readLong();
+ int dataLength = source.readInt();
+ mData = new byte[dataLength];
+ source.readByteArray(mData);
}
- public String getName() {
- return mName;
+ /** Returns the event ID */
+ public int getEventId() {
+ return mEventId;
}
- public String getText() {
- return mText;
+ /** Returns the NPT(Normal Play Time) value when the event occurred or will occur */
+ public long getNpt() {
+ return mNpt;
}
- public String getData() {
+ /** Returns the application specific data */
+ @NonNull
+ public byte[] getData() {
return mData;
}
- public String getStatus() {
- return mStatus;
- }
-
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
super.writeToParcel(dest, flags);
- dest.writeString(mName);
- dest.writeString(mText);
- dest.writeString(mData);
- dest.writeString(mStatus);
+ dest.writeInt(mEventId);
+ dest.writeLong(mNpt);
+ dest.writeInt(mData.length);
+ dest.writeByteArray(mData);
}
}
diff --git a/media/java/android/media/tv/TvContentRatingSystemInfo.java b/media/java/android/media/tv/TvContentRatingSystemInfo.java
index f44ded3dbd37..947b2d67bfce 100644
--- a/media/java/android/media/tv/TvContentRatingSystemInfo.java
+++ b/media/java/android/media/tv/TvContentRatingSystemInfo.java
@@ -94,8 +94,8 @@ public final class TvContentRatingSystemInfo implements Parcelable {
};
private TvContentRatingSystemInfo(Parcel in) {
- mXmlUri = in.readParcelable(null);
- mApplicationInfo = in.readParcelable(null);
+ mXmlUri = in.readParcelable(null, android.net.Uri.class);
+ mApplicationInfo = in.readParcelable(null, android.content.pm.ApplicationInfo.class);
}
@Override
diff --git a/media/java/android/media/tv/TvInputInfo.java b/media/java/android/media/tv/TvInputInfo.java
index 54cb2bff5566..e60d5378f88c 100644
--- a/media/java/android/media/tv/TvInputInfo.java
+++ b/media/java/android/media/tv/TvInputInfo.java
@@ -653,16 +653,16 @@ public final class TvInputInfo implements Parcelable {
mType = in.readInt();
mIsHardwareInput = in.readByte() == 1;
mLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
- mIconUri = in.readParcelable(null);
+ mIconUri = in.readParcelable(null, android.net.Uri.class);
mLabelResId = in.readInt();
- mIcon = in.readParcelable(null);
- mIconStandby = in.readParcelable(null);
- mIconDisconnected = in.readParcelable(null);
+ mIcon = in.readParcelable(null, android.graphics.drawable.Icon.class);
+ mIconStandby = in.readParcelable(null, android.graphics.drawable.Icon.class);
+ mIconDisconnected = in.readParcelable(null, android.graphics.drawable.Icon.class);
mSetupActivity = in.readString();
mCanRecord = in.readByte() == 1;
mCanPauseRecording = in.readByte() == 1;
mTunerCount = in.readInt();
- mHdmiDeviceInfo = in.readParcelable(null);
+ mHdmiDeviceInfo = in.readParcelable(null, android.hardware.hdmi.HdmiDeviceInfo.class);
mIsConnectedToHdmiSwitch = in.readByte() == 1;
mHdmiConnectionRelativePosition = in.readInt();
mParentId = in.readString();
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index 98d1599e62e9..f438d293ac8e 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -31,7 +31,7 @@ import android.graphics.Rect;
import android.media.AudioDeviceInfo;
import android.media.AudioFormat.Encoding;
import android.media.PlaybackParams;
-import android.media.tv.interactive.TvIAppManager;
+import android.media.tv.interactive.TvInteractiveAppManager;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
@@ -2318,7 +2318,7 @@ public final class TvInputManager {
// @GuardedBy("mMetadataLock")
private int mVideoHeight;
- private TvIAppManager.Session mIAppSession;
+ private TvInteractiveAppManager.Session mIAppSession;
private boolean mIAppNotificationEnabled = false;
private Session(IBinder token, InputChannel channel, ITvInputManager service, int userId,
@@ -2331,11 +2331,11 @@ public final class TvInputManager {
mSessionCallbackRecordMap = sessionCallbackRecordMap;
}
- public TvIAppManager.Session getInteractiveAppSession() {
+ public TvInteractiveAppManager.Session getInteractiveAppSession() {
return mIAppSession;
}
- public void setInteractiveAppSession(TvIAppManager.Session iAppSession) {
+ public void setInteractiveAppSession(TvInteractiveAppManager.Session iAppSession) {
this.mIAppSession = iAppSession;
}
@@ -2593,9 +2593,9 @@ public final class TvInputManager {
/**
* Enables interactive app notification.
+ *
* @param enabled {@code true} if you want to enable interactive app notifications.
* {@code false} otherwise.
- * @hide
*/
public void setInteractiveAppNotificationEnabled(boolean enabled) {
if (mToken == null) {
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index 524ba34685b5..9bc736743ecc 100755
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -945,7 +945,13 @@ public abstract class TvInputService extends Service {
}
/**
- * Notifies AIT info updated.
+ * Informs the app that the AIT (Application Information Table) is updated.
+ *
+ * <p>This method should also be call when
+ * {@link #onSetInteractiveAppNotificationEnabled(boolean)} is called to send the first AIT
+ * info.
+ *
+ * @see #onSetInteractiveAppNotificationEnabled(boolean)
* @hide
*/
public void notifyAitInfoUpdated(@NonNull final AitInfo aitInfo) {
@@ -1198,7 +1204,16 @@ public abstract class TvInputService extends Service {
/**
* Enables or disables interactive app notification.
+ *
+ * <p>This method enables or disables the event detection from the corresponding TV input.
+ * When it's enabled, the TV input service detects events related to interactive app, such
+ * as AIT (Application Information Table) and sends to TvView or the linked TV interactive
+ * app service.
+ *
* @param enabled {@code true} to enable, {@code false} to disable.
+ *
+ * @see TvView#setInteractiveAppNotificationEnabled(boolean)
+ * @see Session#notifyAitInfoUpdated(android.media.tv.AitInfo)
* @hide
*/
public void onSetInteractiveAppNotificationEnabled(boolean enabled) {
diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java
index 71f6ad6dd034..d2086c502a5c 100644
--- a/media/java/android/media/tv/TvView.java
+++ b/media/java/android/media/tv/TvView.java
@@ -481,9 +481,18 @@ public class TvView extends ViewGroup {
}
/**
- * Enables interactive app notification.
+ * Enables or disables interactive app notification.
+ *
+ * <p>This method enables or disables the event detection from the corresponding TV input. When
+ * it's enabled, the TV input service detects events related to interactive app, such as
+ * AIT (Application Information Table) and sends to TvView or the linked TV interactive app
+ * service.
+ *
* @param enabled {@code true} if you want to enable interactive app notifications.
* {@code false} otherwise.
+ *
+ * @see TvInputService.Session#notifyAitInfoUpdated(android.media.tv.AitInfo)
+ * @see android.media.tv.interactive.TvInteractiveAppView#setTvView(TvView)
* @hide
*/
public void setInteractiveAppNotificationEnabled(boolean enabled) {
@@ -1062,12 +1071,12 @@ public class TvView extends ViewGroup {
}
/**
- * This is called when the AIT info has been updated.
+ * This is called when the AIT (Application Information Table) info has been updated.
*
* @param aitInfo The current AIT info.
* @hide
*/
- public void onAitInfoUpdated(String inputId, AitInfo aitInfo) {
+ public void onAitInfoUpdated(@NonNull String inputId, @NonNull AitInfo aitInfo) {
}
/**
diff --git a/media/java/android/media/tv/interactive/AppLinkInfo.aidl b/media/java/android/media/tv/interactive/AppLinkInfo.aidl
new file mode 100644
index 000000000000..7c52d018a3d6
--- /dev/null
+++ b/media/java/android/media/tv/interactive/AppLinkInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.interactive;
+
+parcelable AppLinkInfo; \ No newline at end of file
diff --git a/media/java/android/media/tv/interactive/AppLinkInfo.java b/media/java/android/media/tv/interactive/AppLinkInfo.java
new file mode 100644
index 000000000000..5cce44319e5d
--- /dev/null
+++ b/media/java/android/media/tv/interactive/AppLinkInfo.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 android.media.tv.interactive;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * App link information used by TV interactive app to launch Android apps.
+ * @hide
+ */
+public final class AppLinkInfo implements Parcelable {
+ private @NonNull String mPackageName;
+ private @NonNull String mClassName;
+ private @Nullable String mUriScheme;
+ private @Nullable String mUriHost;
+ private @Nullable String mUriPrefix;
+
+
+ /**
+ * Creates a new AppLinkInfo.
+ */
+ private AppLinkInfo(
+ @NonNull String packageName,
+ @NonNull String className,
+ @Nullable String uriScheme,
+ @Nullable String uriHost,
+ @Nullable String uriPrefix) {
+ this.mPackageName = packageName;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mPackageName);
+ this.mClassName = className;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mClassName);
+ this.mUriScheme = uriScheme;
+ this.mUriHost = uriHost;
+ this.mUriPrefix = uriPrefix;
+ }
+
+ /**
+ * Gets package name of the App link.
+ */
+ @NonNull
+ public String getPackageName() {
+ return mPackageName;
+ }
+
+ /**
+ * Gets package class of the App link.
+ */
+ @NonNull
+ public String getClassName() {
+ return mClassName;
+ }
+
+ /**
+ * Gets URI scheme of the App link.
+ */
+ @Nullable
+ public String getUriScheme() {
+ return mUriScheme;
+ }
+
+ /**
+ * Gets URI host of the App link.
+ */
+ @Nullable
+ public String getUriHost() {
+ return mUriHost;
+ }
+
+ /**
+ * Gets URI prefix of the App link.
+ */
+ @Nullable
+ public String getUriPrefix() {
+ return mUriPrefix;
+ }
+
+ @Override
+ public String toString() {
+ return "AppLinkInfo { "
+ + "packageName = " + mPackageName + ", "
+ + "className = " + mClassName + ", "
+ + "uriScheme = " + mUriScheme + ", "
+ + "uriHost = " + mUriHost + ", "
+ + "uriPrefix = " + mUriPrefix
+ + " }";
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString(mPackageName);
+ dest.writeString(mClassName);
+ dest.writeString(mUriScheme);
+ dest.writeString(mUriHost);
+ dest.writeString(mUriPrefix);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /* package-private */ AppLinkInfo(@NonNull Parcel in) {
+ String packageName = in.readString();
+ String className = in.readString();
+ String uriScheme = in.readString();
+ String uriHost = in.readString();
+ String uriPrefix = in.readString();
+
+ this.mPackageName = packageName;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mPackageName);
+ this.mClassName = className;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mClassName);
+ this.mUriScheme = uriScheme;
+ this.mUriHost = uriHost;
+ this.mUriPrefix = uriPrefix;
+ }
+
+ @NonNull
+ public static final Parcelable.Creator<AppLinkInfo> CREATOR =
+ new Parcelable.Creator<AppLinkInfo>() {
+ @Override
+ public AppLinkInfo[] newArray(int size) {
+ return new AppLinkInfo[size];
+ }
+
+ @Override
+ public AppLinkInfo createFromParcel(@NonNull Parcel in) {
+ return new AppLinkInfo(in);
+ }
+ };
+
+ /**
+ * A builder for {@link AppLinkInfo}
+ */
+ public static final class Builder {
+ private @NonNull String mPackageName;
+ private @NonNull String mClassName;
+ private @Nullable String mUriScheme;
+ private @Nullable String mUriHost;
+ private @Nullable String mUriPrefix;
+
+ /**
+ * Creates a new Builder.
+ */
+ public Builder(
+ @NonNull String packageName,
+ @NonNull String className) {
+ mPackageName = packageName;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mPackageName);
+ mClassName = className;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mClassName);
+ }
+
+ /**
+ * Sets package name of the App link.
+ */
+ @NonNull
+ public Builder setPackageName(@NonNull String value) {
+ mPackageName = value;
+ return this;
+ }
+
+ /**
+ * Sets app name of the App link.
+ */
+ @NonNull
+ public Builder setClassName(@NonNull String value) {
+ mClassName = value;
+ return this;
+ }
+
+ /**
+ * Sets URI scheme of the App link.
+ */
+ @NonNull
+ public Builder setUriScheme(@Nullable String value) {
+ mUriScheme = value;
+ return this;
+ }
+
+ /**
+ * Sets URI host of the App link.
+ */
+ @NonNull
+ public Builder setUriHost(@Nullable String value) {
+ mUriHost = value;
+ return this;
+ }
+
+ /**
+ * Sets URI prefix of the App link.
+ */
+ @NonNull
+ public Builder setUriPrefix(@Nullable String value) {
+ mUriPrefix = value;
+ return this;
+ }
+
+ /** Builds the instance. This builder should not be touched after calling this! */
+ @NonNull
+ public AppLinkInfo build() {
+ AppLinkInfo o = new AppLinkInfo(
+ mPackageName,
+ mClassName,
+ mUriScheme,
+ mUriHost,
+ mUriPrefix);
+ return o;
+ }
+ }
+}
diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl
index 1a8fc4671ec3..a3e58d16f655 100644
--- a/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl
@@ -34,7 +34,7 @@ oneway interface ITvInteractiveAppClient {
void onLayoutSurface(int left, int top, int right, int bottom, int seq);
void onBroadcastInfoRequest(in BroadcastInfoRequest request, int seq);
void onRemoveBroadcastInfo(int id, int seq);
- void onSessionStateChanged(int state, int seq);
+ void onSessionStateChanged(int state, int err, int seq);
void onBiInteractiveAppCreated(in Uri biIAppUri, in String biIAppId, int seq);
void onTeletextAppStateChanged(int state, int seq);
void onCommandRequest(in String cmdType, in Bundle parameters, int seq);
diff --git a/media/java/android/media/tv/interactive/ITvIAppManager.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppManager.aidl
index a19a2d2d6135..aaabe342d9f1 100644
--- a/media/java/android/media/tv/interactive/ITvIAppManager.aidl
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppManager.aidl
@@ -20,6 +20,7 @@ import android.graphics.Rect;
import android.media.tv.AdResponse;
import android.media.tv.BroadcastInfoResponse;
import android.media.tv.TvTrackInfo;
+import android.media.tv.interactive.AppLinkInfo;
import android.media.tv.interactive.ITvInteractiveAppClient;
import android.media.tv.interactive.ITvInteractiveAppManagerCallback;
import android.media.tv.interactive.TvInteractiveAppInfo;
@@ -31,11 +32,11 @@ import android.view.Surface;
* Interface to the TV interactive app service.
* @hide
*/
-interface ITvIAppManager {
+interface ITvInteractiveAppManager {
List<TvInteractiveAppInfo> getTvInteractiveAppServiceList(int userId);
void prepare(String tiasId, int type, int userId);
- void registerAppLinkInfo(String tiasId, in Bundle info, int userId);
- void unregisterAppLinkInfo(String tiasId, in Bundle info, int userId);
+ void registerAppLinkInfo(String tiasId, in AppLinkInfo info, int userId);
+ void unregisterAppLinkInfo(String tiasId, in AppLinkInfo info, int userId);
void sendAppLinkCommand(String tiasId, in Bundle command, int userId);
void startInteractiveApp(in IBinder sessionToken, int userId);
void stopInteractiveApp(in IBinder sessionToken, int userId);
diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppManagerCallback.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppManagerCallback.aidl
index f4510f6c60a3..23be4c64fcc4 100644
--- a/media/java/android/media/tv/interactive/ITvInteractiveAppManagerCallback.aidl
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppManagerCallback.aidl
@@ -27,5 +27,5 @@ interface ITvInteractiveAppManagerCallback {
void onInteractiveAppServiceRemoved(in String iAppServiceId);
void onInteractiveAppServiceUpdated(in String iAppServiceId);
void onTvInteractiveAppInfoUpdated(in TvInteractiveAppInfo tvIAppInfo);
- void onStateChanged(in String iAppServiceId, int type, int state);
+ void onStateChanged(in String iAppServiceId, int type, int state, int err);
} \ No newline at end of file
diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppService.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppService.aidl
index c1e66229670a..b6d518ff7242 100644
--- a/media/java/android/media/tv/interactive/ITvInteractiveAppService.aidl
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppService.aidl
@@ -16,6 +16,7 @@
package android.media.tv.interactive;
+import android.media.tv.interactive.AppLinkInfo;
import android.media.tv.interactive.ITvInteractiveAppServiceCallback;
import android.media.tv.interactive.ITvInteractiveAppSessionCallback;
import android.os.Bundle;
@@ -23,7 +24,7 @@ import android.view.InputChannel;
/**
* Top-level interface to a TV Interactive App component (implemented in a Service). It's used for
- * TvIAppManagerService to communicate with TvIAppService.
+ * TvInteractiveAppManagerService to communicate with TvInteractiveAppService.
* @hide
*/
oneway interface ITvInteractiveAppService {
@@ -32,7 +33,7 @@ oneway interface ITvInteractiveAppService {
void createSession(in InputChannel channel, in ITvInteractiveAppSessionCallback callback,
in String iAppServiceId, int type);
void prepare(int type);
- void registerAppLinkInfo(in Bundle info);
- void unregisterAppLinkInfo(in Bundle info);
+ void registerAppLinkInfo(in AppLinkInfo info);
+ void unregisterAppLinkInfo(in AppLinkInfo info);
void sendAppLinkCommand(in Bundle command);
} \ No newline at end of file
diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppServiceCallback.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppServiceCallback.aidl
index f56d3bd284da..970b94327572 100644
--- a/media/java/android/media/tv/interactive/ITvInteractiveAppServiceCallback.aidl
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppServiceCallback.aidl
@@ -17,10 +17,10 @@
package android.media.tv.interactive;
/**
- * Helper interface for ITvInteractiveAppService to allow the TvIAppService to notify the
- * TvIAppManagerService.
+ * Helper interface for ITvInteractiveAppService to allow the TvInteractiveAppService to notify the
+ * TvInteractiveAppManagerService.
* @hide
*/
oneway interface ITvInteractiveAppServiceCallback {
- void onStateChanged(int type, int state);
+ void onStateChanged(int type, int state, int error);
} \ No newline at end of file
diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl
index c270424b4067..385f0d4f766a 100644
--- a/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl
@@ -24,7 +24,7 @@ import android.net.Uri;
import android.os.Bundle;
/**
- * Helper interface for ITvInteractiveAppSession to allow TvIAppService to notify the
+ * Helper interface for ITvInteractiveAppSession to allow TvInteractiveAppService to notify the
* system service when there is a related event.
* @hide
*/
@@ -33,7 +33,7 @@ oneway interface ITvInteractiveAppSessionCallback {
void onLayoutSurface(int left, int top, int right, int bottom);
void onBroadcastInfoRequest(in BroadcastInfoRequest request);
void onRemoveBroadcastInfo(int id);
- void onSessionStateChanged(int state);
+ void onSessionStateChanged(int state, int err);
void onBiInteractiveAppCreated(in Uri biIAppUri, in String biIAppId);
void onTeletextAppStateChanged(int state);
void onCommandRequest(in String cmdType, in Bundle parameters);
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppInfo.java b/media/java/android/media/tv/interactive/TvInteractiveAppInfo.java
index 2f96552f6f32..e1f535c93d19 100644
--- a/media/java/android/media/tv/interactive/TvInteractiveAppInfo.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppInfo.java
@@ -44,7 +44,6 @@ import java.util.List;
/**
* This class is used to specify meta information of a TV interactive app.
- * @hide
*/
public final class TvInteractiveAppInfo implements Parcelable {
private static final boolean DEBUG = false;
@@ -59,7 +58,7 @@ public final class TvInteractiveAppInfo implements Parcelable {
INTERACTIVE_APP_TYPE_ATSC,
INTERACTIVE_APP_TYPE_GINGA,
})
- @interface InteractiveAppType {}
+ public @interface InteractiveAppType {}
/** HbbTV interactive app type */
public static final int INTERACTIVE_APP_TYPE_HBBTV = 0x1;
@@ -77,7 +76,7 @@ public final class TvInteractiveAppInfo implements Parcelable {
throw new IllegalArgumentException("context cannot be null.");
}
Intent intent =
- new Intent(TvIAppService.SERVICE_INTERFACE).setComponent(component);
+ new Intent(TvInteractiveAppService.SERVICE_INTERFACE).setComponent(component);
ResolveInfo resolveInfo = context.getPackageManager().resolveService(intent,
PackageManager.GET_SERVICES | PackageManager.GET_META_DATA);
if (resolveInfo == null) {
@@ -171,10 +170,10 @@ public final class TvInteractiveAppInfo implements Parcelable {
ServiceInfo si = resolveInfo.serviceInfo;
PackageManager pm = context.getPackageManager();
try (XmlResourceParser parser =
- si.loadXmlMetaData(pm, TvIAppService.SERVICE_META_DATA)) {
+ si.loadXmlMetaData(pm, TvInteractiveAppService.SERVICE_META_DATA)) {
if (parser == null) {
throw new IllegalStateException(
- "No " + TvIAppService.SERVICE_META_DATA
+ "No " + TvInteractiveAppService.SERVICE_META_DATA
+ " meta-data found for " + si.name);
}
@@ -194,9 +193,9 @@ public final class TvInteractiveAppInfo implements Parcelable {
}
TypedArray sa = res.obtainAttributes(attrs,
- com.android.internal.R.styleable.TvIAppService);
+ com.android.internal.R.styleable.TvInteractiveAppService);
CharSequence[] textArr = sa.getTextArray(
- com.android.internal.R.styleable.TvIAppService_supportedTypes);
+ com.android.internal.R.styleable.TvInteractiveAppService_supportedTypes);
for (CharSequence cs : textArr) {
types.add(cs.toString().toLowerCase());
}
diff --git a/media/java/android/media/tv/interactive/TvIAppManager.java b/media/java/android/media/tv/interactive/TvInteractiveAppManager.java
index f819438944f4..39be501a072d 100755
--- a/media/java/android/media/tv/interactive/TvIAppManager.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppManager.java
@@ -16,6 +16,7 @@
package android.media.tv.interactive;
+import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -52,45 +53,128 @@ import java.lang.annotation.RetentionPolicy;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
+import java.util.concurrent.Executor;
/**
* Central system API to the overall TV interactive application framework (TIAF) architecture, which
* arbitrates interaction between applications and interactive apps.
*/
-@SystemService(Context.TV_IAPP_SERVICE)
-public final class TvIAppManager {
+@SystemService(Context.TV_INTERACTIVE_APP_SERVICE)
+public final class TvInteractiveAppManager {
// TODO: cleanup and unhide public APIs
- private static final String TAG = "TvIAppManager";
+ private static final String TAG = "TvInteractiveAppManager";
/** @hide */
@Retention(RetentionPolicy.SOURCE)
- @IntDef(flag = false, prefix = "TV_INTERACTIVE_APP_RTE_STATE_", value = {
- TV_INTERACTIVE_APP_RTE_STATE_UNREALIZED,
- TV_INTERACTIVE_APP_RTE_STATE_PREPARING,
- TV_INTERACTIVE_APP_RTE_STATE_READY,
- TV_INTERACTIVE_APP_RTE_STATE_ERROR})
- public @interface TvInteractiveAppRteState {}
+ @IntDef(flag = false, prefix = "SERVICE_STATE_", value = {
+ SERVICE_STATE_UNREALIZED,
+ SERVICE_STATE_PREPARING,
+ SERVICE_STATE_READY,
+ SERVICE_STATE_ERROR})
+ public @interface ServiceState {}
/**
- * Unrealized state of interactive app RTE.
+ * Unrealized state of interactive app service.
* @hide
*/
- public static final int TV_INTERACTIVE_APP_RTE_STATE_UNREALIZED = 1;
+ public static final int SERVICE_STATE_UNREALIZED = 1;
/**
- * Preparing state of interactive app RTE.
+ * Preparing state of interactive app service.
* @hide
*/
- public static final int TV_INTERACTIVE_APP_RTE_STATE_PREPARING = 2;
+ public static final int SERVICE_STATE_PREPARING = 2;
/**
- * Ready state of interactive app RTE.
+ * Ready state of interactive app service.
* @hide
*/
- public static final int TV_INTERACTIVE_APP_RTE_STATE_READY = 3;
+ public static final int SERVICE_STATE_READY = 3;
/**
- * Error state of interactive app RTE.
+ * Error state of interactive app service.
* @hide
*/
- public static final int TV_INTERACTIVE_APP_RTE_STATE_ERROR = 4;
+ public static final int SERVICE_STATE_ERROR = 4;
+
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = false, prefix = "INTERACTIVE_APP_STATE_", value = {
+ INTERACTIVE_APP_STATE_STOPPED,
+ INTERACTIVE_APP_STATE_RUNNING,
+ INTERACTIVE_APP_STATE_ERROR})
+ public @interface InteractiveAppState {}
+
+ /**
+ * Stopped (or not started) state of interactive application.
+ * @hide
+ */
+ public static final int INTERACTIVE_APP_STATE_STOPPED = 1;
+ /**
+ * Running state of interactive application.
+ * @hide
+ */
+ public static final int INTERACTIVE_APP_STATE_RUNNING = 2;
+ /**
+ * Error state of interactive application.
+ * @hide
+ */
+ public static final int INTERACTIVE_APP_STATE_ERROR = 3;
+
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = false, prefix = "ERROR_", value = {
+ ERROR_NONE,
+ ERROR_UNKNOWN,
+ ERROR_NOT_SUPPORTED,
+ ERROR_WEAK_SIGNAL,
+ ERROR_RESOURCE_UNAVAILABLE,
+ ERROR_BLOCKED,
+ ERROR_ENCRYPTED,
+ ERROR_UNKNOWN_CHANNEL,
+ })
+ public @interface ErrorCode {}
+
+ /**
+ * No error.
+ * @hide
+ */
+ public static final int ERROR_NONE = 0;
+ /**
+ * Unknown error code.
+ * @hide
+ */
+ public static final int ERROR_UNKNOWN = 1;
+ /**
+ * Error code for an unsupported channel.
+ * @hide
+ */
+ public static final int ERROR_NOT_SUPPORTED = 2;
+ /**
+ * Error code for weak signal.
+ * @hide
+ */
+ public static final int ERROR_WEAK_SIGNAL = 3;
+ /**
+ * Error code when resource (e.g. tuner) is unavailable.
+ * @hide
+ */
+ public static final int ERROR_RESOURCE_UNAVAILABLE = 4;
+ /**
+ * Error code for blocked contents.
+ * @hide
+ */
+ public static final int ERROR_BLOCKED = 5;
+ /**
+ * Error code when the key or module is missing for the encrypted channel.
+ * @hide
+ */
+ public static final int ERROR_ENCRYPTED = 6;
+ /**
+ * Error code when the current channel is an unknown channel.
+ * @hide
+ */
+ public static final int ERROR_UNKNOWN_CHANNEL = 7;
+
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@@ -190,7 +274,7 @@ public final class TvIAppManager {
*/
public static final String KEY_BACK_URI = "back_uri";
- private final ITvIAppManager mService;
+ private final ITvInteractiveAppManager mService;
private final int mUserId;
// A mapping from the sequence number of a session to its SessionCallbackRecord.
@@ -209,7 +293,7 @@ public final class TvIAppManager {
private final ITvInteractiveAppClient mClient;
/** @hide */
- public TvIAppManager(ITvIAppManager service, int userId) {
+ public TvInteractiveAppManager(ITvInteractiveAppManager service, int userId) {
mService = service;
mUserId = userId;
mClient = new ITvInteractiveAppClient.Stub() {
@@ -285,7 +369,7 @@ public final class TvIAppManager {
@Override
public void onCommandRequest(
- @TvIAppService.InteractiveAppServiceCommandType String cmdType,
+ @TvInteractiveAppService.InteractiveAppServiceCommandType String cmdType,
Bundle parameters,
int seq) {
synchronized (mSessionCallbackRecordMap) {
@@ -383,14 +467,14 @@ public final class TvIAppManager {
}
@Override
- public void onSessionStateChanged(int state, int seq) {
+ public void onSessionStateChanged(int state, int err, int seq) {
synchronized (mSessionCallbackRecordMap) {
SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
if (record == null) {
Log.e(TAG, "Callback not found for seq " + seq);
return;
}
- record.postSessionStateChanged(state);
+ record.postSessionStateChanged(state, err);
}
}
@@ -458,10 +542,10 @@ public final class TvIAppManager {
}
@Override
- public void onStateChanged(String iAppServiceId, int type, int state) {
+ public void onStateChanged(String iAppServiceId, int type, int state, int err) {
synchronized (mLock) {
for (TvInteractiveAppCallbackRecord record : mCallbackRecords) {
- record.postStateChanged(iAppServiceId, type, state);
+ record.postStateChanged(iAppServiceId, type, state, err);
}
}
}
@@ -484,9 +568,10 @@ public final class TvIAppManager {
* This is called when a TV Interactive App service is added to the system.
*
* <p>Normally it happens when the user installs a new TV Interactive App service package
- * that implements {@link TvIAppService} interface.
+ * that implements {@link TvInteractiveAppService} interface.
*
* @param iAppServiceId The ID of the TV Interactive App service.
+ * @hide
*/
public void onInteractiveAppServiceAdded(@NonNull String iAppServiceId) {
}
@@ -498,6 +583,7 @@ public final class TvIAppManager {
* App service package.
*
* @param iAppServiceId The ID of the TV Interactive App service.
+ * @hide
*/
public void onInteractiveAppServiceRemoved(@NonNull String iAppServiceId) {
}
@@ -509,6 +595,7 @@ public final class TvIAppManager {
* re-installed or a newer version of the package exists becomes available/unavailable.
*
* @param iAppServiceId The ID of the TV Interactive App service.
+ * @hide
*/
public void onInteractiveAppServiceUpdated(@NonNull String iAppServiceId) {
}
@@ -524,26 +611,34 @@ public final class TvIAppManager {
*
* @param iAppInfo The <code>TvInteractiveAppInfo</code> object that contains new
* information.
+ * @hide
*/
public void onTvInteractiveAppInfoUpdated(@NonNull TvInteractiveAppInfo iAppInfo) {
}
/**
* This is called when the state of the interactive app service is changed.
- * @hide
+ *
+ * @param type the interactive app type
+ * @param state the current state of the service of the given type
+ * @param err the error code for error state. {@link #ERROR_NONE} is used when the state is
+ * not {@link #SERVICE_STATE_ERROR}.
*/
public void onTvInteractiveAppServiceStateChanged(
- @NonNull String iAppServiceId, int type, @TvInteractiveAppRteState int state) {
+ @NonNull String iAppServiceId,
+ @TvInteractiveAppInfo.InteractiveAppType int type,
+ @ServiceState int state,
+ @ErrorCode int err) {
}
}
private static final class TvInteractiveAppCallbackRecord {
private final TvInteractiveAppCallback mCallback;
- private final Handler mHandler;
+ private final Executor mExecutor;
- TvInteractiveAppCallbackRecord(TvInteractiveAppCallback callback, Handler handler) {
+ TvInteractiveAppCallbackRecord(TvInteractiveAppCallback callback, Executor executor) {
mCallback = callback;
- mHandler = handler;
+ mExecutor = executor;
}
public TvInteractiveAppCallback getCallback() {
@@ -551,7 +646,7 @@ public final class TvIAppManager {
}
public void postInteractiveAppServiceAdded(final String iAppServiceId) {
- mHandler.post(new Runnable() {
+ mExecutor.execute(new Runnable() {
@Override
public void run() {
mCallback.onInteractiveAppServiceAdded(iAppServiceId);
@@ -560,7 +655,7 @@ public final class TvIAppManager {
}
public void postInteractiveAppServiceRemoved(final String iAppServiceId) {
- mHandler.post(new Runnable() {
+ mExecutor.execute(new Runnable() {
@Override
public void run() {
mCallback.onInteractiveAppServiceRemoved(iAppServiceId);
@@ -569,7 +664,7 @@ public final class TvIAppManager {
}
public void postInteractiveAppServiceUpdated(final String iAppServiceId) {
- mHandler.post(new Runnable() {
+ mExecutor.execute(new Runnable() {
@Override
public void run() {
mCallback.onInteractiveAppServiceUpdated(iAppServiceId);
@@ -578,7 +673,7 @@ public final class TvIAppManager {
}
public void postTvInteractiveAppInfoUpdated(final TvInteractiveAppInfo iAppInfo) {
- mHandler.post(new Runnable() {
+ mExecutor.execute(new Runnable() {
@Override
public void run() {
mCallback.onTvInteractiveAppInfoUpdated(iAppInfo);
@@ -586,11 +681,12 @@ public final class TvIAppManager {
});
}
- public void postStateChanged(String iAppServiceId, int type, int state) {
- mHandler.post(new Runnable() {
+ public void postStateChanged(String iAppServiceId, int type, int state, int err) {
+ mExecutor.execute(new Runnable() {
@Override
public void run() {
- mCallback.onTvInteractiveAppServiceStateChanged(iAppServiceId, type, state);
+ mCallback.onTvInteractiveAppServiceStateChanged(
+ iAppServiceId, type, state, err);
}
});
}
@@ -635,7 +731,6 @@ public final class TvIAppManager {
*
* @return List of {@link TvInteractiveAppInfo} for each TV Interactive App service that
* describes its meta information.
- * @hide
*/
@NonNull
public List<TvInteractiveAppInfo> getTvInteractiveAppServiceList() {
@@ -662,7 +757,8 @@ public final class TvIAppManager {
* Registers app link info.
* @hide
*/
- public void registerAppLinkInfo(@NonNull String tvIAppServiceId, @NonNull Bundle appLinkInfo) {
+ public void registerAppLinkInfo(
+ @NonNull String tvIAppServiceId, @NonNull AppLinkInfo appLinkInfo) {
try {
mService.registerAppLinkInfo(tvIAppServiceId, appLinkInfo, mUserId);
} catch (RemoteException e) {
@@ -675,7 +771,7 @@ public final class TvIAppManager {
* @hide
*/
public void unregisterAppLinkInfo(
- @NonNull String tvIAppServiceId, @NonNull Bundle appLinkInfo) {
+ @NonNull String tvIAppServiceId, @NonNull AppLinkInfo appLinkInfo) {
try {
mService.unregisterAppLinkInfo(tvIAppServiceId, appLinkInfo, mUserId);
} catch (RemoteException e) {
@@ -699,15 +795,16 @@ public final class TvIAppManager {
* Registers a {@link TvInteractiveAppCallback}.
*
* @param callback A callback used to monitor status of the TV Interactive App services.
- * @param handler A {@link Handler} that the status change will be delivered to.
+ * @param executor A {@link Executor} that the status change will be delivered to.
* @hide
*/
public void registerCallback(
- @NonNull TvInteractiveAppCallback callback, @NonNull Handler handler) {
+ @NonNull TvInteractiveAppCallback callback,
+ @CallbackExecutor @NonNull Executor executor) {
Preconditions.checkNotNull(callback);
- Preconditions.checkNotNull(handler);
+ Preconditions.checkNotNull(executor);
synchronized (mLock) {
- mCallbackRecords.add(new TvInteractiveAppCallbackRecord(callback, handler));
+ mCallbackRecords.add(new TvInteractiveAppCallbackRecord(callback, executor));
}
}
@@ -742,7 +839,7 @@ public final class TvIAppManager {
private static final long INPUT_SESSION_NOT_RESPONDING_TIMEOUT = 2500;
- private final ITvIAppManager mService;
+ private final ITvInteractiveAppManager mService;
private final int mUserId;
private final int mSeq;
private final SparseArray<SessionCallbackRecord> mSessionCallbackRecordMap;
@@ -759,7 +856,7 @@ public final class TvIAppManager {
private TvInputEventSender mSender;
private InputChannel mInputChannel;
- private Session(IBinder token, InputChannel channel, ITvIAppManager service,
+ private Session(IBinder token, InputChannel channel, ITvInteractiveAppManager service,
int userId, int seq, SparseArray<SessionCallbackRecord> sessionCallbackRecordMap) {
mToken = token;
mInputChannel = channel;
@@ -1142,7 +1239,7 @@ public final class TvIAppManager {
}
/**
- * Notifies IAPP session when video is available.
+ * Notifies Interactive APP session when video is available.
*/
public void notifyVideoAvailable() {
if (mToken == null) {
@@ -1157,7 +1254,7 @@ public final class TvIAppManager {
}
/**
- * Notifies IAPP session when video is unavailable.
+ * Notifies Interactive APP session when video is unavailable.
*/
public void notifyVideoUnavailable(int reason) {
if (mToken == null) {
@@ -1172,7 +1269,7 @@ public final class TvIAppManager {
}
/**
- * Notifies IAPP session when content is allowed.
+ * Notifies Interactive APP session when content is allowed.
*/
public void notifyContentAllowed() {
if (mToken == null) {
@@ -1187,7 +1284,7 @@ public final class TvIAppManager {
}
/**
- * Notifies IAPP session when content is blocked.
+ * Notifies Interactive APP session when content is blocked.
*/
public void notifyContentBlocked(TvContentRating rating) {
if (mToken == null) {
@@ -1478,7 +1575,7 @@ public final class TvIAppManager {
}
void postCommandRequest(
- final @TvIAppService.InteractiveAppServiceCommandType String cmdType,
+ final @TvInteractiveAppService.InteractiveAppServiceCommandType String cmdType,
final Bundle parameters) {
mHandler.post(new Runnable() {
@Override
@@ -1553,11 +1650,11 @@ public final class TvIAppManager {
});
}
- void postSessionStateChanged(int state) {
+ void postSessionStateChanged(int state, int err) {
mHandler.post(new Runnable() {
@Override
public void run() {
- mSessionCallback.onSessionStateChanged(mSession, state);
+ mSessionCallback.onSessionStateChanged(mSession, state, err);
}
});
}
@@ -1587,28 +1684,28 @@ public final class TvIAppManager {
*/
public abstract static class SessionCallback {
/**
- * This is called after {@link TvIAppManager#createSession} has been processed.
+ * This is called after {@link TvInteractiveAppManager#createSession} has been processed.
*
- * @param session A {@link TvIAppManager.Session} instance created. This can be
+ * @param session A {@link TvInteractiveAppManager.Session} instance created. This can be
* {@code null} if the creation request failed.
*/
public void onSessionCreated(@Nullable Session session) {
}
/**
- * This is called when {@link TvIAppManager.Session} is released.
+ * This is called when {@link TvInteractiveAppManager.Session} is released.
* This typically happens when the process hosting the session has crashed or been killed.
*
- * @param session the {@link TvIAppManager.Session} instance released.
+ * @param session the {@link TvInteractiveAppManager.Session} instance released.
*/
public void onSessionReleased(@NonNull Session session) {
}
/**
- * This is called when {@link TvIAppService.Session#layoutSurface} is called to
+ * This is called when {@link TvInteractiveAppService.Session#layoutSurface} is called to
* change the layout of surface.
*
- * @param session A {@link TvIAppManager.Session} associated with this callback.
+ * @param session A {@link TvInteractiveAppManager.Session} associated with this callback.
* @param left Left position.
* @param top Top position.
* @param right Right position.
@@ -1618,85 +1715,90 @@ public final class TvIAppManager {
}
/**
- * This is called when {@link TvIAppService.Session#requestCommand} is called.
+ * This is called when {@link TvInteractiveAppService.Session#requestCommand} is called.
*
- * @param session A {@link TvIAppManager.Session} associated with this callback.
+ * @param session A {@link TvInteractiveAppManager.Session} associated with this callback.
* @param cmdType type of the command.
* @param parameters parameters of the command.
*/
public void onCommandRequest(
Session session,
- @TvIAppService.InteractiveAppServiceCommandType String cmdType,
+ @TvInteractiveAppService.InteractiveAppServiceCommandType String cmdType,
Bundle parameters) {
}
/**
- * This is called when {@link TvIAppService.Session#SetVideoBounds} is called.
+ * This is called when {@link TvInteractiveAppService.Session#SetVideoBounds} is called.
*
- * @param session A {@link TvIAppManager.Session} associated with this callback.
+ * @param session A {@link TvInteractiveAppManager.Session} associated with this callback.
*/
public void onSetVideoBounds(Session session, Rect rect) {
}
/**
- * This is called when {@link TvIAppService.Session#RequestCurrentChannelUri} is
+ * This is called when {@link TvInteractiveAppService.Session#RequestCurrentChannelUri} is
* called.
*
- * @param session A {@link TvIAppManager.Session} associated with this callback.
+ * @param session A {@link TvInteractiveAppManager.Session} associated with this callback.
*/
public void onRequestCurrentChannelUri(Session session) {
}
/**
- * This is called when {@link TvIAppService.Session#RequestCurrentChannelLcn} is
+ * This is called when {@link TvInteractiveAppService.Session#RequestCurrentChannelLcn} is
* called.
*
- * @param session A {@link TvIAppManager.Session} associated with this callback.
+ * @param session A {@link TvInteractiveAppManager.Session} associated with this callback.
*/
public void onRequestCurrentChannelLcn(Session session) {
}
/**
- * This is called when {@link TvIAppService.Session#RequestStreamVolume} is
+ * This is called when {@link TvInteractiveAppService.Session#RequestStreamVolume} is
* called.
*
- * @param session A {@link TvIAppManager.Session} associated with this callback.
+ * @param session A {@link TvInteractiveAppManager.Session} associated with this callback.
*/
public void onRequestStreamVolume(Session session) {
}
/**
- * This is called when {@link TvIAppService.Session#RequestTrackInfoList} is
+ * This is called when {@link TvInteractiveAppService.Session#RequestTrackInfoList} is
* called.
*
- * @param session A {@link TvIAppManager.Session} associated with this callback.
+ * @param session A {@link TvInteractiveAppManager.Session} associated with this callback.
*/
public void onRequestTrackInfoList(Session session) {
}
/**
- * This is called when {@link TvIAppService.Session#RequestCurrentTvInputId} is called.
+ * This is called when {@link TvInteractiveAppService.Session#RequestCurrentTvInputId} is
+ * called.
*
- * @param session A {@link TvIAppManager.Session} associated with this callback.
+ * @param session A {@link TvInteractiveAppService.Session} associated with this callback.
* @hide
*/
public void onRequestCurrentTvInputId(Session session) {
}
/**
- * This is called when {@link TvIAppService.Session#notifySessionStateChanged} is called.
+ * This is called when {@link TvInteractiveAppService.Session#notifySessionStateChanged} is
+ * called.
*
- * @param session A {@link TvIAppManager.Session} associated with this callback.
+ * @param session A {@link TvInteractiveAppManager.Session} associated with this callback.
* @param state the current state.
*/
- public void onSessionStateChanged(Session session, int state) {
+ public void onSessionStateChanged(
+ Session session,
+ @InteractiveAppState int state,
+ @ErrorCode int err) {
}
/**
- * This is called when {@link TvIAppService.Session#notifyBiInteractiveAppCreated}
+ * This is called when {@link TvInteractiveAppService.Session#notifyBiInteractiveAppCreated}
* is called.
*
- * @param session A {@link TvIAppManager.Session} associated with this callback.
+ * @param session A {@link TvInteractiveAppManager.Session} associated with this callback.
* @param biIAppUri URI associated this BI interactive app. This is the same URI in
* {@link Session#createBiInteractiveApp(Uri, Bundle)}
* @param biIAppId BI interactive app ID, which can be used to destroy the BI interactive
@@ -1709,11 +1811,11 @@ public final class TvIAppManager {
* This is called when {@link TvIAppService.Session#notifyTeletextAppStateChanged} is
* called.
*
- * @param session A {@link TvIAppManager.Session} associated with this callback.
+ * @param session A {@link TvInteractiveAppManager.Session} associated with this callback.
* @param state the current state.
*/
public void onTeletextAppStateChanged(
- Session session, @TvIAppManager.TeletextAppState int state) {
+ Session session, @TvInteractiveAppManager.TeletextAppState int state) {
}
}
}
diff --git a/media/java/android/media/tv/interactive/TvIAppService.java b/media/java/android/media/tv/interactive/TvInteractiveAppService.java
index c0ec76b10655..d599d0a60995 100755
--- a/media/java/android/media/tv/interactive/TvIAppService.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppService.java
@@ -65,11 +65,11 @@ import java.util.ArrayList;
import java.util.List;
/**
- * The TvIAppService class represents a TV interactive applications RTE.
+ * The TvInteractiveAppService class represents a TV interactive applications RTE.
*/
-public abstract class TvIAppService extends Service {
+public abstract class TvInteractiveAppService extends Service {
private static final boolean DEBUG = false;
- private static final String TAG = "TvIAppService";
+ private static final String TAG = "TvInteractiveAppService";
private static final int DETACH_MEDIA_VIEW_TIMEOUT_MS = 5000;
@@ -83,12 +83,12 @@ public abstract class TvIAppService extends Service {
* cannot abuse it.
*/
public static final String SERVICE_INTERFACE =
- "android.media.tv.interactive.TvIAppService";
+ "android.media.tv.interactive.TvInteractiveAppService";
/**
- * Name under which a TvIAppService component publishes information about itself. This
+ * Name under which a TvInteractiveAppService component publishes information about itself. This
* meta-data must reference an XML resource containing an
- * <code>&lt;{@link android.R.styleable#TvIAppService tv-interactive-app}&gt;</code>
+ * <code>&lt;{@link android.R.styleable#TvInteractiveAppService tv-interactive-app}&gt;</code>
* tag.
*/
public static final String SERVICE_META_DATA = "android.media.tv.interactive.app";
@@ -131,6 +131,13 @@ public abstract class TvIAppService extends Service {
/** @hide */
public static final String COMMAND_PARAMETER_KEY_TRACK_SELECT_MODE =
"command_track_select_mode";
+ /**
+ * Command to quiet channel change. No channel banner or channel info is shown.
+ * <p>Refer to HbbTV Spec 2.0.4 chapter A.2.4.3.
+ * @hide
+ */
+ public static final String COMMAND_PARAMETER_KEY_CHANGE_CHANNEL_QUIETLY =
+ "command_change_channel_quietly";
private final Handler mServiceHandler = new ServiceHandler();
private final RemoteCallbackList<ITvInteractiveAppServiceCallback> mCallbacks =
@@ -175,12 +182,12 @@ public abstract class TvIAppService extends Service {
}
@Override
- public void registerAppLinkInfo(Bundle appLinkInfo) {
+ public void registerAppLinkInfo(AppLinkInfo appLinkInfo) {
onRegisterAppLinkInfo(appLinkInfo);
}
@Override
- public void unregisterAppLinkInfo(Bundle appLinkInfo) {
+ public void unregisterAppLinkInfo(AppLinkInfo appLinkInfo) {
onUnregisterAppLinkInfo(appLinkInfo);
}
@@ -196,15 +203,14 @@ public abstract class TvIAppService extends Service {
* Prepares TV Interactive App service for the given type.
* @hide
*/
- public void onPrepare(int type) {
- // TODO: make it abstract when unhide
+ public void onPrepare(@TvInteractiveAppInfo.InteractiveAppType int type) {
}
/**
* Registers App link info.
* @hide
*/
- public void onRegisterAppLinkInfo(Bundle appLinkInfo) {
+ public void onRegisterAppLinkInfo(AppLinkInfo appLinkInfo) {
// TODO: make it abstract when unhide
}
@@ -212,7 +218,7 @@ public abstract class TvIAppService extends Service {
* Unregisters App link info.
* @hide
*/
- public void onUnregisterAppLinkInfo(Bundle appLinkInfo) {
+ public void onUnregisterAppLinkInfo(AppLinkInfo appLinkInfo) {
// TODO: make it abstract when unhide
}
@@ -236,20 +242,32 @@ public abstract class TvIAppService extends Service {
* @hide
*/
@Nullable
- public Session onCreateSession(@NonNull String iAppServiceId, int type) {
- // TODO: make it abstract when unhide
+ public Session onCreateSession(
+ @NonNull String iAppServiceId,
+ @TvInteractiveAppInfo.InteractiveAppType int type) {
return null;
}
/**
- * Notifies the system when the state of the interactive app has been changed.
- * @param state the current state
+ * Notifies the system when the state of the interactive app RTE has been changed.
+ *
+ * @param type the interactive app type
+ * @param state the current state of the service of the given type
+ * @param error the error code for error state. {@link TvInteractiveAppManager#ERROR_NONE} is
+ * used when the state is not
+ * {@link TvInteractiveAppManager#SERVICE_STATE_ERROR}.
* @hide
*/
public final void notifyStateChanged(
- int type, @TvIAppManager.TvInteractiveAppRteState int state) {
- mServiceHandler.obtainMessage(ServiceHandler.DO_NOTIFY_RTE_STATE_CHANGED,
- type, state).sendToTarget();
+ @TvInteractiveAppInfo.InteractiveAppType int type,
+ @TvInteractiveAppManager.ServiceState int state,
+ @TvInteractiveAppManager.ErrorCode int error) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = type;
+ args.arg2 = state;
+ args.arg3 = error;
+ mServiceHandler
+ .obtainMessage(ServiceHandler.DO_NOTIFY_RTE_STATE_CHANGED, args).sendToTarget();
}
/**
@@ -298,6 +316,7 @@ public abstract class TvIAppService extends Service {
*
* @param enable {@code true} if you want to enable the media view. {@code false}
* otherwise.
+ * @hide
*/
public void setMediaViewEnabled(final boolean enable) {
mHandler.post(new Runnable() {
@@ -319,15 +338,13 @@ public abstract class TvIAppService extends Service {
}
/**
- * Starts TvIAppService session.
- * @hide
+ * Starts TvInteractiveAppService session.
*/
public void onStartInteractiveApp() {
}
/**
- * Stops TvIAppService session.
- * @hide
+ * Stops TvInteractiveAppService session.
*/
public void onStopInteractiveApp() {
}
@@ -364,6 +381,7 @@ public abstract class TvIAppService extends Service {
/**
* To toggle Digital Teletext Application if there is one in AIT app list.
* @param enable
+ * @hide
*/
public void onSetTeletextAppEnabled(boolean enable) {
}
@@ -438,6 +456,7 @@ public abstract class TvIAppService extends Service {
*
* @param width The width of the media view.
* @param height The height of the media view.
+ * @hide
*/
public void onMediaViewSizeChanged(int width, int height) {
}
@@ -447,6 +466,7 @@ public abstract class TvIAppService extends Service {
* implementation can override this method and return its own view.
*
* @return a view attached to the media window
+ * @hide
*/
@Nullable
public View onCreateMediaView() {
@@ -454,7 +474,7 @@ public abstract class TvIAppService extends Service {
}
/**
- * Releases TvIAppService session.
+ * Releases TvInteractiveAppService session.
* @hide
*/
public void onRelease() {
@@ -620,6 +640,7 @@ public abstract class TvIAppService extends Service {
/**
* Requests broadcast related information from the related TV input.
* @param request the request for broadcast info
+ * @hide
*/
public void requestBroadcastInfo(@NonNull final BroadcastInfoRequest request) {
executeOrPostRunnableOnMainThread(new Runnable() {
@@ -644,6 +665,7 @@ public abstract class TvIAppService extends Service {
/**
* Remove broadcast information request from the related TV input.
* @param requestId the ID of the request
+ * @hide
*/
public void removeBroadcastInfo(final int requestId) {
executeOrPostRunnableOnMainThread(new Runnable() {
@@ -669,6 +691,7 @@ public abstract class TvIAppService extends Service {
* requests a specific command to be processed by the related TV input.
* @param cmdType type of the specific command
* @param parameters parameters of the specific command
+ * @hide
*/
public void requestCommand(
@InteractiveAppServiceCommandType String cmdType, Bundle parameters) {
@@ -693,6 +716,7 @@ public abstract class TvIAppService extends Service {
/**
* Sets broadcast video bounds.
+ * @hide
*/
public void setVideoBounds(Rect rect) {
executeOrPostRunnableOnMainThread(new Runnable() {
@@ -715,6 +739,7 @@ public abstract class TvIAppService extends Service {
/**
* Requests the URI of the current channel.
+ * @hide
*/
public void requestCurrentChannelUri() {
executeOrPostRunnableOnMainThread(new Runnable() {
@@ -737,6 +762,7 @@ public abstract class TvIAppService extends Service {
/**
* Requests the logic channel number (LCN) of the current channel.
+ * @hide
*/
public void requestCurrentChannelLcn() {
executeOrPostRunnableOnMainThread(new Runnable() {
@@ -759,6 +785,7 @@ public abstract class TvIAppService extends Service {
/**
* Requests stream volume.
+ * @hide
*/
public void requestStreamVolume() {
executeOrPostRunnableOnMainThread(new Runnable() {
@@ -781,6 +808,7 @@ public abstract class TvIAppService extends Service {
/**
* Requests the list of {@link TvTrackInfo}.
+ * @hide
*/
public void requestTrackInfoList() {
executeOrPostRunnableOnMainThread(new Runnable() {
@@ -829,6 +857,7 @@ public abstract class TvIAppService extends Service {
/**
* requests an advertisement request to be processed by the related TV input.
* @param request advertisement request
+ * @hide
*/
public void requestAd(@NonNull final AdRequest request) {
executeOrPostRunnableOnMainThread(new Runnable() {
@@ -987,10 +1016,15 @@ public abstract class TvIAppService extends Service {
/**
* Notifies when the session state is changed.
- * @param state the current state.
+ *
+ * @param state the current session state.
+ * @param err the error code for error state. {@link TvInteractiveAppManager#ERROR_NONE} is
+ * used when the state is not
+ * {@link TvInteractiveAppManager#INTERACTIVE_APP_STATE_ERROR}.
*/
public void notifySessionStateChanged(
- @TvIAppManager.TvInteractiveAppRteState int state) {
+ @TvInteractiveAppManager.InteractiveAppState int state,
+ @TvInteractiveAppManager.ErrorCode int err) {
executeOrPostRunnableOnMainThread(new Runnable() {
@MainThread
@Override
@@ -998,10 +1032,10 @@ public abstract class TvIAppService extends Service {
try {
if (DEBUG) {
Log.d(TAG, "notifySessionStateChanged (state="
- + state + ")");
+ + state + "; err=" + err + ")");
}
if (mSessionCallback != null) {
- mSessionCallback.onSessionStateChanged(state);
+ mSessionCallback.onSessionStateChanged(state, err);
}
} catch (RemoteException e) {
Log.w(TAG, "error in notifySessionStateChanged", e);
@@ -1039,8 +1073,10 @@ public abstract class TvIAppService extends Service {
/**
* Notifies when the digital teletext app state is changed.
* @param state the current state.
+ * @hide
*/
- public final void notifyTeletextAppStateChanged(@TvIAppManager.TeletextAppState int state) {
+ public final void notifyTeletextAppStateChanged(
+ @TvInteractiveAppManager.TeletextAppState int state) {
executeOrPostRunnableOnMainThread(new Runnable() {
@MainThread
@Override
@@ -1068,7 +1104,7 @@ public abstract class TvIAppService extends Service {
if (event instanceof KeyEvent) {
KeyEvent keyEvent = (KeyEvent) event;
if (keyEvent.dispatch(this, mDispatcherState, this)) {
- return TvIAppManager.Session.DISPATCH_HANDLED;
+ return TvInteractiveAppManager.Session.DISPATCH_HANDLED;
}
// TODO: special handlings of navigation keys and media keys
@@ -1077,20 +1113,20 @@ public abstract class TvIAppService extends Service {
final int source = motionEvent.getSource();
if (motionEvent.isTouchEvent()) {
if (onTouchEvent(motionEvent)) {
- return TvIAppManager.Session.DISPATCH_HANDLED;
+ return TvInteractiveAppManager.Session.DISPATCH_HANDLED;
}
} else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
if (onTrackballEvent(motionEvent)) {
- return TvIAppManager.Session.DISPATCH_HANDLED;
+ return TvInteractiveAppManager.Session.DISPATCH_HANDLED;
}
} else {
if (onGenericMotionEvent(motionEvent)) {
- return TvIAppManager.Session.DISPATCH_HANDLED;
+ return TvInteractiveAppManager.Session.DISPATCH_HANDLED;
}
}
}
// TODO: handle overlay view
- return TvIAppManager.Session.DISPATCH_NOT_HANDLED;
+ return TvInteractiveAppManager.Session.DISPATCH_NOT_HANDLED;
}
private void initialize(ITvInteractiveAppSessionCallback callback) {
@@ -1443,9 +1479,9 @@ public abstract class TvIAppService extends Service {
}
int handled = mSessionImpl.dispatchInputEvent(event, this);
- if (handled != TvIAppManager.Session.DISPATCH_IN_PROGRESS) {
+ if (handled != TvInteractiveAppManager.Session.DISPATCH_IN_PROGRESS) {
finishInputEvent(
- event, handled == TvIAppManager.Session.DISPATCH_HANDLED);
+ event, handled == TvInteractiveAppManager.Session.DISPATCH_HANDLED);
}
}
}
@@ -1457,11 +1493,11 @@ public abstract class TvIAppService extends Service {
private static final int DO_NOTIFY_SESSION_CREATED = 2;
private static final int DO_NOTIFY_RTE_STATE_CHANGED = 3;
- private void broadcastRteStateChanged(int type, int state) {
+ private void broadcastRteStateChanged(int type, int state, int error) {
int n = mCallbacks.beginBroadcast();
for (int i = 0; i < n; ++i) {
try {
- mCallbacks.getBroadcastItem(i).onStateChanged(type, state);
+ mCallbacks.getBroadcastItem(i).onStateChanged(type, state, error);
} catch (RemoteException e) {
Log.e(TAG, "error in broadcastRteStateChanged", e);
}
@@ -1491,7 +1527,7 @@ public abstract class TvIAppService extends Service {
return;
}
ITvInteractiveAppSession stub = new ITvInteractiveAppSessionWrapper(
- android.media.tv.interactive.TvIAppService.this, sessionImpl, channel);
+ TvInteractiveAppService.this, sessionImpl, channel);
SomeArgs someArgs = SomeArgs.obtain();
someArgs.arg1 = sessionImpl;
@@ -1519,9 +1555,11 @@ public abstract class TvIAppService extends Service {
return;
}
case DO_NOTIFY_RTE_STATE_CHANGED: {
- int type = msg.arg1;
- int state = msg.arg2;
- broadcastRteStateChanged(type, state);
+ SomeArgs args = (SomeArgs) msg.obj;
+ int type = (int) args.arg1;
+ int state = (int) args.arg2;
+ int error = (int) args.arg3;
+ broadcastRteStateChanged(type, state, error);
return;
}
default: {
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppView.java b/media/java/android/media/tv/interactive/TvInteractiveAppView.java
index 6f99d515f1e4..12e21998f226 100755
--- a/media/java/android/media/tv/interactive/TvInteractiveAppView.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppView.java
@@ -22,14 +22,15 @@ import android.annotation.Nullable;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
+import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.RectF;
import android.media.tv.TvInputManager;
import android.media.tv.TvTrackInfo;
import android.media.tv.TvView;
-import android.media.tv.interactive.TvIAppManager.Session;
-import android.media.tv.interactive.TvIAppManager.Session.FinishedInputEventCallback;
-import android.media.tv.interactive.TvIAppManager.SessionCallback;
+import android.media.tv.interactive.TvInteractiveAppManager.Session;
+import android.media.tv.interactive.TvInteractiveAppManager.Session.FinishedInputEventCallback;
+import android.media.tv.interactive.TvInteractiveAppManager.SessionCallback;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
@@ -50,7 +51,6 @@ import java.util.concurrent.Executor;
/**
* Displays contents of interactive TV applications.
- * @hide
*/
public class TvInteractiveAppView extends ViewGroup {
private static final String TAG = "TvInteractiveAppView";
@@ -61,7 +61,7 @@ public class TvInteractiveAppView extends ViewGroup {
private static final int UNSET_TVVIEW_SUCCESS = 3;
private static final int UNSET_TVVIEW_FAIL = 4;
- private final TvIAppManager mTvInteractiveAppManager;
+ private final TvInteractiveAppManager mTvInteractiveAppManager;
private final Handler mHandler = new Handler();
private final Object mCallbackLock = new Object();
private Session mSession;
@@ -141,8 +141,8 @@ public class TvInteractiveAppView extends ViewGroup {
}
mDefStyleAttr = defStyleAttr;
resetSurfaceView();
- mTvInteractiveAppManager = (TvIAppManager) getContext().getSystemService(
- Context.TV_IAPP_SERVICE);
+ mTvInteractiveAppManager = (TvInteractiveAppManager) getContext().getSystemService(
+ Context.TV_INTERACTIVE_APP_SERVICE);
}
/**
@@ -152,8 +152,8 @@ public class TvInteractiveAppView extends ViewGroup {
* callback.
*/
public void setCallback(
- @NonNull TvInteractiveAppCallback callback,
- @NonNull @CallbackExecutor Executor executor) {
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull TvInteractiveAppCallback callback) {
synchronized (mCallbackLock) {
mCallbackExecutor = executor;
mCallback = callback;
@@ -238,6 +238,7 @@ public class TvInteractiveAppView extends ViewGroup {
// The surface view's content should be treated as secure all the time.
mSurfaceView.setSecure(true);
mSurfaceView.getHolder().addCallback(mSurfaceHolderCallback);
+ mSurfaceView.getHolder().setFormat(PixelFormat.TRANSLUCENT);
addView(mSurfaceView);
}
@@ -381,9 +382,16 @@ public class TvInteractiveAppView extends ViewGroup {
/**
* Prepares the interactive application.
+ *
+ * @param iAppServiceId the interactive app service ID, which can be found in
+ * {@link TvInteractiveAppInfo#getId()}.
+ *
+ * @see android.media.tv.interactive.TvInteractiveAppManager#getTvInteractiveAppServiceList()
* @hide
*/
- public void prepareInteractiveApp(@NonNull String iAppServiceId, int type) {
+ public void prepareInteractiveApp(
+ @NonNull String iAppServiceId,
+ @TvInteractiveAppInfo.InteractiveAppType int type) {
// TODO: document and handle the cases that this method is called multiple times.
if (DEBUG) {
Log.d(TAG, "prepareInteractiveApp");
@@ -552,10 +560,10 @@ public class TvInteractiveAppView extends ViewGroup {
/**
* Sets the TvInteractiveAppView to receive events from TIS. This method links the session of
- * TvIAppManager to TvInputManager session, so the TIAS can get the TIS events.
+ * TvInteractiveAppManager to TvInputManager session, so the TIAS can get the TIS events.
*
* @param tvView the TvView to be linked to this TvInteractiveAppView via linking of Sessions.
- * @return to be added
+ * @return The result of the operation.
* @hide
*/
public int setTvView(@Nullable TvView tvView) {
@@ -583,6 +591,7 @@ public class TvInteractiveAppView extends ViewGroup {
/**
* To toggle Digital Teletext Application if there is one in AIT app list.
* @param enable
+ * @hide
*/
public void setTeletextAppEnabled(boolean enable) {
if (DEBUG) {
@@ -609,7 +618,7 @@ public class TvInteractiveAppView extends ViewGroup {
*/
public void onCommandRequest(
@NonNull String iAppServiceId,
- @NonNull @TvIAppService.InteractiveAppServiceCommandType String cmdType,
+ @NonNull @TvInteractiveAppService.InteractiveAppServiceCommandType String cmdType,
@Nullable Bundle parameters) {
}
@@ -617,10 +626,16 @@ public class TvInteractiveAppView extends ViewGroup {
* This is called when the session state is changed.
*
* @param iAppServiceId The ID of the TV interactive app service bound to this view.
- * @param state current session state.
+ * @param state the current state.
+ * @param err the error code for error state. {@link TvInteractiveAppManager#ERROR_NONE}
+ * is used when the state is not
+ * {@link TvInteractiveAppManager#INTERACTIVE_APP_STATE_ERROR}.
* @hide
*/
- public void onSessionStateChanged(@NonNull String iAppServiceId, int state) {
+ public void onStateChanged(
+ @NonNull String iAppServiceId,
+ @TvInteractiveAppManager.InteractiveAppState int state,
+ @TvInteractiveAppManager.ErrorCode int err) {
}
/**
@@ -642,13 +657,15 @@ public class TvInteractiveAppView extends ViewGroup {
*
* @param iAppServiceId The ID of the TV interactive app service bound to this view.
* @param state digital teletext app current state.
+ * @hide
*/
public void onTeletextAppStateChanged(
- @NonNull String iAppServiceId, @TvIAppManager.TeletextAppState int state) {
+ @NonNull String iAppServiceId,
+ @TvInteractiveAppManager.TeletextAppState int state) {
}
/**
- * This is called when {@link TvIAppService.Session#SetVideoBounds} is called.
+ * This is called when {@link TvInteractiveAppService.Session#SetVideoBounds} is called.
*
* @param iAppServiceId The ID of the TV interactive app service bound to this view.
* @hide
@@ -657,7 +674,7 @@ public class TvInteractiveAppView extends ViewGroup {
}
/**
- * This is called when {@link TvIAppService.Session#RequestCurrentChannelUri} is
+ * This is called when {@link TvInteractiveAppService.Session#RequestCurrentChannelUri} is
* called.
*
* @param iAppServiceId The ID of the TV interactive app service bound to this view.
@@ -667,7 +684,7 @@ public class TvInteractiveAppView extends ViewGroup {
}
/**
- * This is called when {@link TvIAppService.Session#RequestCurrentChannelLcn} is
+ * This is called when {@link TvInteractiveAppService.Session#RequestCurrentChannelLcn} is
* called.
*
* @param iAppServiceId The ID of the TV interactive app service bound to this view.
@@ -677,7 +694,7 @@ public class TvInteractiveAppView extends ViewGroup {
}
/**
- * This is called when {@link TvIAppService.Session#RequestStreamVolume} is
+ * This is called when {@link TvInteractiveAppService.Session#RequestStreamVolume} is
* called.
*
* @param iAppServiceId The ID of the TV interactive app service bound to this view.
@@ -687,7 +704,7 @@ public class TvInteractiveAppView extends ViewGroup {
}
/**
- * This is called when {@link TvIAppService.Session#RequestTrackInfoList} is
+ * This is called when {@link TvInteractiveAppService.Session#RequestTrackInfoList} is
* called.
*
* @param iAppServiceId The ID of the TV interactive app service bound to this view.
@@ -700,6 +717,7 @@ public class TvInteractiveAppView extends ViewGroup {
* This is called when {@link TvIAppService.Session#RequestCurrentTvInputId} is called.
*
* @param iAppServiceId The ID of the TV interactive app service bound to this view.
+ * @hide
*/
public void onRequestCurrentTvInputId(@NonNull String iAppServiceId) {
}
@@ -801,7 +819,7 @@ public class TvInteractiveAppView extends ViewGroup {
@Override
public void onCommandRequest(
Session session,
- @TvIAppService.InteractiveAppServiceCommandType String cmdType,
+ @TvInteractiveAppService.InteractiveAppServiceCommandType String cmdType,
Bundle parameters) {
if (DEBUG) {
Log.d(TAG, "onCommandRequest (cmdType=" + cmdType + ", parameters="
@@ -825,9 +843,12 @@ public class TvInteractiveAppView extends ViewGroup {
}
@Override
- public void onSessionStateChanged(Session session, int state) {
+ public void onSessionStateChanged(
+ Session session,
+ @TvInteractiveAppManager.InteractiveAppState int state,
+ @TvInteractiveAppManager.ErrorCode int err) {
if (DEBUG) {
- Log.d(TAG, "onSessionStateChanged (state=" + state + ")");
+ Log.d(TAG, "onSessionStateChanged (state=" + state + "; err=" + err + ")");
}
if (this != mSessionCallback) {
Log.w(TAG, "onSessionStateChanged - session not created");
@@ -838,7 +859,7 @@ public class TvInteractiveAppView extends ViewGroup {
mCallbackExecutor.execute(() -> {
synchronized (mCallbackLock) {
if (mCallback != null) {
- mCallback.onSessionStateChanged(mIAppServiceId, state);
+ mCallback.onStateChanged(mIAppServiceId, state, err);
}
}
});
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index 9c4a83a235c0..315737572c19 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -1002,7 +1002,7 @@ public class Tuner implements AutoCloseable {
private native String nativeGetFrontendHardwareInfo();
private native int nativeSetMaxNumberOfFrontends(int frontendType, int maxNumber);
private native int nativeGetMaxNumberOfFrontends(int frontendType);
-
+ private native int nativeRemoveOutputPid(int pid);
private native Lnb nativeOpenLnbByHandle(int handle);
private native Lnb nativeOpenLnbByName(String name);
@@ -1565,6 +1565,36 @@ public class Tuner implements AutoCloseable {
}
/**
+ * Filter out unnecessary PID (packet identifier) from frontend output.
+ *
+ * <p>It is used by the client to remove some video or audio PIDs of other program to reduce the
+ * total amount of recorded TS.
+ *
+ * <p>This API is only supported by Tuner HAL 2.0 or higher. Unsupported version would cause
+ * no-op. Use {@link TunerVersionChecker#getTunerVersion()} to check the version.
+ *
+ * @return result status of the operation. Unsupported version or if current active frontend
+ * doesn’t support PID filtering out would return {@link #RESULT_UNAVAILABLE}.
+ * @throws IllegalStateException if there is no active frontend currently.
+ */
+ @Result
+ public int removeOutputPid(@IntRange(from = 0) int pid) {
+ mFrontendLock.lock();
+ try {
+ if (!TunerVersionChecker.checkHigherOrEqualVersionTo(
+ TunerVersionChecker.TUNER_VERSION_2_0, "Remove output PID")) {
+ return RESULT_UNAVAILABLE;
+ }
+ if (mFrontend == null) {
+ throw new IllegalStateException("frontend is not initialized");
+ }
+ return nativeRemoveOutputPid(pid);
+ } finally {
+ mFrontendLock.unlock();
+ }
+ }
+
+ /**
* Gets the currently initialized and activated frontend information. To get all the available
* frontend info on the device, use {@link getAvailableFrontendInfos()}.
*
diff --git a/media/java/android/media/tv/tuner/filter/SectionSettings.java b/media/java/android/media/tv/tuner/filter/SectionSettings.java
index f123675a8940..83ed8e84e4da 100644
--- a/media/java/android/media/tv/tuner/filter/SectionSettings.java
+++ b/media/java/android/media/tv/tuner/filter/SectionSettings.java
@@ -82,7 +82,7 @@ public abstract class SectionSettings extends Settings {
* The section filter uses this for CRC (Cyclic redundancy check) checking when
* {@link #isCrcEnabled()} is {@code true}.
*/
- public int getBitWidthOfLengthField() {
+ public int getLengthFieldBitWidth() {
return mBitWidthOfLengthField;
}
diff --git a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
index 8cedd04a8b89..c1e9b38a13b0 100644
--- a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
+++ b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
@@ -54,7 +54,7 @@ public class FrontendStatus {
FRONTEND_STATUS_TYPE_IS_MISO_ENABLED, FRONTEND_STATUS_TYPE_IS_LINEAR,
FRONTEND_STATUS_TYPE_IS_SHORT_FRAMES_ENABLED, FRONTEND_STATUS_TYPE_ISDBT_MODE,
FRONTEND_STATUS_TYPE_ISDBT_PARTIAL_RECEPTION_FLAG, FRONTEND_STATUS_TYPE_STREAM_IDS,
- FRONTEND_STATUS_TYPE_DVBT_CELL_IDS})
+ FRONTEND_STATUS_TYPE_DVBT_CELL_IDS, FRONTEND_STATUS_TYPE_ATSC3_ALL_PLP_INFO})
@Retention(RetentionPolicy.SOURCE)
public @interface FrontendStatusType {}
@@ -165,7 +165,7 @@ public class FrontendStatus {
public static final int FRONTEND_STATUS_TYPE_RF_LOCK =
android.hardware.tv.tuner.FrontendStatusType.RF_LOCK;
/**
- * PLP information in a frequency band for ATSC-3.0 frontend.
+ * Current tuned PLP information in a frequency band for ATSC-3.0 frontend.
*/
public static final int FRONTEND_STATUS_TYPE_ATSC3_PLP_INFO =
android.hardware.tv.tuner.FrontendStatusType.ATSC3_PLP_INFO;
@@ -267,6 +267,13 @@ public class FrontendStatus {
public static final int FRONTEND_STATUS_TYPE_DVBT_CELL_IDS =
android.hardware.tv.tuner.FrontendStatusType.DVBT_CELL_IDS;
+ /**
+ * All PLP information in a frequency band for ATSC-3.0 frontend, which includes both tuned and
+ * not tuned PLPs for currently watching service.
+ */
+ public static final int FRONTEND_STATUS_TYPE_ATSC3_ALL_PLP_INFO =
+ android.hardware.tv.tuner.FrontendStatusType.ATSC3_ALL_PLP_INFO;
+
/** @hide */
@IntDef(value = {
AtscFrontendSettings.MODULATION_UNDEFINED,
@@ -508,6 +515,7 @@ public class FrontendStatus {
private Integer mIsdbtPartialReceptionFlag;
private int[] mStreamIds;
private int[] mDvbtCellIds;
+ private Atsc3PlpInfo[] mAllPlpInfo;
// Constructed and fields set by JNI code.
private FrontendStatus() {
@@ -1078,6 +1086,25 @@ public class FrontendStatus {
}
/**
+ * Gets an array of all PLPs information of ATSC3 frontend, which includes both tuned and not
+ * tuned PLPs for currently watching service.
+ *
+ * <p>This query is only supported by Tuner HAL 2.0 or higher. Unsupported version or if HAL
+ * doesn't return all PLPs information will throw IllegalStateException. Use
+ * {@link TunerVersionChecker#getTunerVersion()} to check the version.
+ */
+ @SuppressLint("ArrayReturn")
+ @NonNull
+ public Atsc3PlpInfo[] getAllAtsc3PlpInfo() {
+ TunerVersionChecker.checkHigherOrEqualVersionTo(
+ TunerVersionChecker.TUNER_VERSION_2_0, "Atsc3PlpInfo all status");
+ if (mAllPlpInfo == null) {
+ throw new IllegalStateException("Atsc3PlpInfo all status is empty");
+ }
+ return mAllPlpInfo;
+ }
+
+ /**
* Information of each tuning Physical Layer Pipes.
*/
public static class Atsc3PlpTuningInfo {
diff --git a/media/jni/android_media_ImageWriter.cpp b/media/jni/android_media_ImageWriter.cpp
index 0a5490d33293..2e419a61de91 100644
--- a/media/jni/android_media_ImageWriter.cpp
+++ b/media/jni/android_media_ImageWriter.cpp
@@ -375,7 +375,8 @@ static void ImageWriter_classInit(JNIEnv* env, jclass clazz) {
}
static jlong ImageWriter_init(JNIEnv* env, jobject thiz, jobject weakThiz, jobject jsurface,
- jint maxImages, jint userFormat, jint userWidth, jint userHeight) {
+ jint maxImages, jint userWidth, jint userHeight, jboolean useSurfaceImageFormatInfo,
+ jint hardwareBufferFormat, jlong dataSpace, jlong ndkUsage) {
status_t res;
ALOGV("%s: maxImages:%d", __FUNCTION__, maxImages);
@@ -450,7 +451,7 @@ static jlong ImageWriter_init(JNIEnv* env, jobject thiz, jobject weakThiz, jobje
// Query surface format if no valid user format is specified, otherwise, override surface format
// with user format.
- if (userFormat == IMAGE_FORMAT_UNKNOWN) {
+ if (useSurfaceImageFormatInfo) {
if ((res = anw->query(anw.get(), NATIVE_WINDOW_FORMAT, &surfaceFormat)) != OK) {
ALOGE("%s: Query Surface format failed: %s (%d)", __FUNCTION__, strerror(-res), res);
jniThrowRuntimeException(env, "Failed to query Surface format");
@@ -458,13 +459,13 @@ static jlong ImageWriter_init(JNIEnv* env, jobject thiz, jobject weakThiz, jobje
}
} else {
// Set consumer buffer format to user specified format
- PublicFormat publicFormat = static_cast<PublicFormat>(userFormat);
- int nativeFormat = mapPublicFormatToHalFormat(publicFormat);
- android_dataspace nativeDataspace = mapPublicFormatToHalDataspace(publicFormat);
- res = native_window_set_buffers_format(anw.get(), nativeFormat);
+ android_dataspace nativeDataspace = static_cast<android_dataspace>(dataSpace);
+ int userFormat = static_cast<int>(mapHalFormatDataspaceToPublicFormat(
+ hardwareBufferFormat, nativeDataspace));
+ res = native_window_set_buffers_format(anw.get(), hardwareBufferFormat);
if (res != OK) {
ALOGE("%s: Unable to configure consumer native buffer format to %#x",
- __FUNCTION__, nativeFormat);
+ __FUNCTION__, hardwareBufferFormat);
jniThrowRuntimeException(env, "Failed to set Surface format");
return 0;
}
@@ -484,15 +485,13 @@ static jlong ImageWriter_init(JNIEnv* env, jobject thiz, jobject weakThiz, jobje
env->SetIntField(thiz,
gImageWriterClassInfo.mWriterFormat, reinterpret_cast<jint>(surfaceFormat));
- if (!isFormatOpaque(surfaceFormat)) {
- res = native_window_set_usage(anw.get(), GRALLOC_USAGE_SW_WRITE_OFTEN);
- if (res != OK) {
- ALOGE("%s: Configure usage %08x for format %08x failed: %s (%d)",
- __FUNCTION__, static_cast<unsigned int>(GRALLOC_USAGE_SW_WRITE_OFTEN),
- surfaceFormat, strerror(-res), res);
- jniThrowRuntimeException(env, "Failed to SW_WRITE_OFTEN configure usage");
- return 0;
- }
+ res = native_window_set_usage(anw.get(), ndkUsage);
+ if (res != OK) {
+ ALOGE("%s: Configure usage %08x for format %08x failed: %s (%d)",
+ __FUNCTION__, static_cast<unsigned int>(ndkUsage),
+ surfaceFormat, strerror(-res), res);
+ jniThrowRuntimeException(env, "Failed to SW_WRITE_OFTEN configure usage");
+ return 0;
}
int minUndequeuedBufferCount = 0;
@@ -1093,7 +1092,7 @@ static jobjectArray Image_createSurfacePlanes(JNIEnv* env, jobject thiz,
static JNINativeMethod gImageWriterMethods[] = {
{"nativeClassInit", "()V", (void*)ImageWriter_classInit },
- {"nativeInit", "(Ljava/lang/Object;Landroid/view/Surface;IIII)J",
+ {"nativeInit", "(Ljava/lang/Object;Landroid/view/Surface;IIIZIJJ)J",
(void*)ImageWriter_init },
{"nativeClose", "(J)V", (void*)ImageWriter_close },
{"nativeAttachAndQueueImage",
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 1b41494814b7..41f3a678577c 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -1622,6 +1622,15 @@ int32_t JTuner::getMaxNumberOfFrontends(int32_t type) {
return mTunerClient->getMaxNumberOfFrontends(static_cast<FrontendType>(type));
}
+jint JTuner::removeOutputPid(int32_t pid) {
+ if (mFeClient == nullptr) {
+ ALOGE("frontend is not initialized");
+ return (jint)Result::INVALID_STATE;
+ }
+
+ return (jint)mFeClient->removeOutputPid(pid);
+}
+
jobject JTuner::openLnbByHandle(int handle) {
if (mTunerClient == nullptr) {
return nullptr;
@@ -2610,6 +2619,24 @@ jobject JTuner::getFrontendStatus(jintArray types) {
env->SetObjectField(statusObj, field, valObj);
break;
}
+ case FrontendStatus::Tag::allPlpInfo: {
+ jfieldID field = env->GetFieldID(clazz, "mAllPlpInfo",
+ "[Landroid/media/tv/tuner/frontend/Atsc3PlpInfo;");
+ jclass plpClazz = env->FindClass("android/media/tv/tuner/frontend/Atsc3PlpInfo");
+ jmethodID initPlp = env->GetMethodID(plpClazz, "<init>", "(IZ)V");
+
+ vector<FrontendScanAtsc3PlpInfo> plpInfos =
+ s.get<FrontendStatus::Tag::allPlpInfo>();
+ jobjectArray valObj = env->NewObjectArray(plpInfos.size(), plpClazz, nullptr);
+ for (int i = 0; i < plpInfos.size(); i++) {
+ jobject plpObj = env->NewObject(plpClazz, initPlp, plpInfos[i].plpId,
+ plpInfos[i].bLlsFlag);
+ env->SetObjectArrayElement(valObj, i, plpObj);
+ }
+
+ env->SetObjectField(statusObj, field, valObj);
+ break;
+ }
}
}
return statusObj;
@@ -4357,6 +4384,11 @@ static jint android_media_tv_Tuner_get_maximum_frontends(JNIEnv *env, jobject th
return tuner->getMaxNumberOfFrontends(type);
}
+static jint android_media_tv_Tuner_remove_output_pid(JNIEnv *env, jobject thiz, jint pid) {
+ sp<JTuner> tuner = getTuner(env, thiz);
+ return tuner->removeOutputPid(pid);
+}
+
static jint android_media_tv_Tuner_close_frontend(JNIEnv* env, jobject thiz, jint /* handle */) {
sp<JTuner> tuner = getTuner(env, thiz);
return tuner->closeFrontend();
@@ -4676,6 +4708,8 @@ static const JNINativeMethod gTunerMethods[] = {
(void *)android_media_tv_Tuner_set_maximum_frontends },
{ "nativeGetMaxNumberOfFrontends", "(I)I",
(void *)android_media_tv_Tuner_get_maximum_frontends },
+ { "nativeRemoveOutputPid", "(I)I",
+ (void *)android_media_tv_Tuner_remove_output_pid },
};
static const JNINativeMethod gFilterMethods[] = {
diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h
index 502bd6b18413..e9475dc3d8ee 100644
--- a/media/jni/android_media_tv_Tuner.h
+++ b/media/jni/android_media_tv_Tuner.h
@@ -205,6 +205,7 @@ struct JTuner : public RefBase {
Result getFrontendHardwareInfo(string& info);
jint setMaxNumberOfFrontends(int32_t frontendType, int32_t maxNumber);
int32_t getMaxNumberOfFrontends(int32_t frontendType);
+ jint removeOutputPid(int32_t pid);
jweak getObject();
diff --git a/media/jni/tuner/FrontendClient.cpp b/media/jni/tuner/FrontendClient.cpp
index 0fdd8d8773f4..bea0342293c0 100644
--- a/media/jni/tuner/FrontendClient.cpp
+++ b/media/jni/tuner/FrontendClient.cpp
@@ -143,6 +143,15 @@ Result FrontendClient::getHardwareInfo(string& info) {
return Result::INVALID_STATE;
}
+Result FrontendClient::removeOutputPid(int32_t pid) {
+ if (mTunerFrontend != nullptr) {
+ Status s = mTunerFrontend->removeOutputPid(pid);
+ return ClientHelper::getServiceSpecificErrorCode(s);
+ }
+
+ return Result::INVALID_STATE;
+}
+
shared_ptr<ITunerFrontend> FrontendClient::getAidlFrontend() {
return mTunerFrontend;
}
diff --git a/media/jni/tuner/FrontendClient.h b/media/jni/tuner/FrontendClient.h
index 77d909804cf0..c6838c85dfc4 100644
--- a/media/jni/tuner/FrontendClient.h
+++ b/media/jni/tuner/FrontendClient.h
@@ -120,6 +120,11 @@ public:
*/
Result getHardwareInfo(string& info);
+ /**
+ * Filter out unnecessary PID from frontend output.
+ */
+ Result removeOutputPid(int32_t pid);
+
int32_t getId();
shared_ptr<ITunerFrontend> getAidlFrontend();
diff --git a/media/native/midi/MidiDeviceInfo.cpp b/media/native/midi/MidiDeviceInfo.cpp
index 8a573fba322b..14524883470f 100644
--- a/media/native/midi/MidiDeviceInfo.cpp
+++ b/media/native/midi/MidiDeviceInfo.cpp
@@ -64,6 +64,7 @@ status_t MidiDeviceInfo::writeToParcel(Parcel* parcel) const {
RETURN_IF_FAILED(writeStringVector(parcel, mInputPortNames));
RETURN_IF_FAILED(writeStringVector(parcel, mOutputPortNames));
RETURN_IF_FAILED(parcel->writeInt32(mIsPrivate ? 1 : 0));
+ RETURN_IF_FAILED(parcel->writeInt32(mDefaultProtocol));
RETURN_IF_FAILED(mProperties.writeToParcel(parcel));
// This corresponds to "extra" properties written by Java code
RETURN_IF_FAILED(mProperties.writeToParcel(parcel));
@@ -83,6 +84,7 @@ status_t MidiDeviceInfo::readFromParcel(const Parcel* parcel) {
int32_t isPrivate;
RETURN_IF_FAILED(parcel->readInt32(&isPrivate));
mIsPrivate = isPrivate == 1;
+ RETURN_IF_FAILED(parcel->readInt32(&mDefaultProtocol));
RETURN_IF_FAILED(mProperties.readFromParcel(parcel));
// Ignore "extra" properties as they may contain Java Parcelables
return OK;
@@ -130,7 +132,8 @@ bool operator==(const MidiDeviceInfo& lhs, const MidiDeviceInfo& rhs) {
areVectorsEqual(lhs.mInputPortNames, rhs.mInputPortNames) &&
areVectorsEqual(lhs.mOutputPortNames, rhs.mOutputPortNames) &&
lhs.mProperties == rhs.mProperties &&
- lhs.mIsPrivate == rhs.mIsPrivate);
+ lhs.mIsPrivate == rhs.mIsPrivate &&
+ lhs.mDefaultProtocol == rhs.mDefaultProtocol);
}
} // namespace midi
diff --git a/media/native/midi/MidiDeviceInfo.h b/media/native/midi/MidiDeviceInfo.h
index 5b4a241323d7..23e1cb474168 100644
--- a/media/native/midi/MidiDeviceInfo.h
+++ b/media/native/midi/MidiDeviceInfo.h
@@ -38,6 +38,7 @@ public:
int getType() const { return mType; }
int getUid() const { return mId; }
bool isPrivate() const { return mIsPrivate; }
+ int getDefaultProtocol() const { return mDefaultProtocol; }
const Vector<String16>& getInputPortNames() const { return mInputPortNames; }
const Vector<String16>& getOutputPortNames() const { return mOutputPortNames; }
String16 getProperty(const char* propertyName);
@@ -48,6 +49,18 @@ public:
TYPE_VIRTUAL = 2,
TYPE_BLUETOOTH = 3,
};
+
+ enum {
+ PROTOCOL_UMP_USE_MIDI_CI = 0,
+ PROTOCOL_UMP_MIDI_1_0_UP_TO_64_BITS = 1,
+ PROTOCOL_UMP_MIDI_1_0_UP_TO_64_BITS_AND_JRTS = 2,
+ PROTOCOL_UMP_MIDI_1_0_UP_TO_128_BITS = 3,
+ PROTOCOL_UMP_MIDI_1_0_UP_TO_128_BITS_AND_JRTS = 4,
+ PROTOCOL_UMP_MIDI_2_0 = 17,
+ PROTOCOL_UMP_MIDI_2_0_AND_JRTS = 18,
+ PROTOCOL_UNKNOWN = -1,
+ };
+
static const char* const PROPERTY_NAME;
static const char* const PROPERTY_MANUFACTURER;
static const char* const PROPERTY_PRODUCT;
@@ -72,6 +85,7 @@ private:
Vector<String16> mOutputPortNames;
os::PersistableBundle mProperties;
bool mIsPrivate;
+ int32_t mDefaultProtocol;
};
} // namespace midi
diff --git a/media/native/midi/amidi.cpp b/media/native/midi/amidi.cpp
index f90796e415c0..aa076e85e30d 100644
--- a/media/native/midi/amidi.cpp
+++ b/media/native/midi/amidi.cpp
@@ -138,6 +138,7 @@ static media_status_t AMIDI_getDeviceInfo(const AMidiDevice *device,
outDeviceInfoPtr->type = deviceInfo.getType();
outDeviceInfoPtr->inputPortCount = deviceInfo.getInputPortNames().size();
outDeviceInfoPtr->outputPortCount = deviceInfo.getOutputPortNames().size();
+ outDeviceInfoPtr->defaultProtocol = deviceInfo.getDefaultProtocol();
return AMEDIA_OK;
}
@@ -238,6 +239,13 @@ ssize_t AMIDI_API AMidiDevice_getNumOutputPorts(const AMidiDevice *device) {
return device->deviceInfo.outputPortCount;
}
+AMidiDevice_Protocol AMIDI_API AMidiDevice_getDefaultProtocol(const AMidiDevice *device) {
+ if (device == nullptr) {
+ return AMIDI_DEVICE_PROTOCOL_UNKNOWN;
+ }
+ return static_cast<AMidiDevice_Protocol>(device->deviceInfo.defaultProtocol);
+}
+
/*
* Port Helpers
*/
diff --git a/media/native/midi/amidi_internal.h b/media/native/midi/amidi_internal.h
index fce85963d217..023a6f5ec900 100644
--- a/media/native/midi/amidi_internal.h
+++ b/media/native/midi/amidi_internal.h
@@ -25,6 +25,7 @@ typedef struct {
int32_t type; /* one of AMIDI_DEVICE_TYPE_* constants */
int32_t inputPortCount; /* number of input (send) ports associated with the device */
int32_t outputPortCount; /* number of output (received) ports associated with the device */
+ int32_t defaultProtocol; /* one of the AMIDI_DEVICE_PROTOCOL_* constants */
} AMidiDeviceInfo;
struct AMidiDevice {
diff --git a/media/native/midi/include/amidi/AMidi.h b/media/native/midi/include/amidi/AMidi.h
index 742db34b74a7..fbb7fb329659 100644
--- a/media/native/midi/include/amidi/AMidi.h
+++ b/media/native/midi/include/amidi/AMidi.h
@@ -62,6 +62,78 @@ enum {
};
/*
+ * Protocol IDs for various MIDI devices.
+ *
+ * Introduced in API 33.
+ */
+enum AMidiDevice_Protocol : int32_t {
+ /**
+ * Constant representing a default protocol with Universal MIDI Packets (UMP).
+ * UMP is defined in "Universal MIDI Packet (UMP) Format and MIDI 2.0 Protocol" spec.
+ * All UMP data should be a multiple of 4 bytes.
+ * Use UMP to negotiate with the device with MIDI-CI.
+ * MIDI-CI is defined in "MIDI Capability Inquiry (MIDI-CI)" spec.
+ */
+ AMIDI_DEVICE_PROTOCOL_UMP_USE_MIDI_CI = 0,
+
+ /**
+ * Constant representing a default protocol with Universal MIDI Packets (UMP).
+ * UMP is defined in "Universal MIDI Packet (UMP) Format and MIDI 2.0 Protocol" spec.
+ * All UMP data should be a multiple of 4 bytes.
+ * Use MIDI 1.0 through UMP with packet sizes up to 64 bits.
+ */
+ AMIDI_DEVICE_PROTOCOL_UMP_MIDI_1_0_UP_TO_64_BITS = 1,
+
+ /**
+ * Constant representing a default protocol with Universal MIDI Packets (UMP).
+ * UMP is defined in "Universal MIDI Packet (UMP) Format and MIDI 2.0 Protocol" spec.
+ * All UMP data should be a multiple of 4 bytes.
+ * Use MIDI 1.0 through UMP with packet sizes up to 64 bits and jitter reduction timestamps.
+ */
+ AMIDI_DEVICE_PROTOCOL_UMP_MIDI_1_0_UP_TO_64_BITS_AND_JRTS = 2,
+
+ /**
+ * Constant representing a default protocol with Universal MIDI Packets (UMP).
+ * UMP is defined in "Universal MIDI Packet (UMP) Format and MIDI 2.0 Protocol" spec.
+ * All UMP data should be a multiple of 4 bytes.
+ * Use MIDI 1.0 through UMP with packet sizes up to 128 bits.
+ */
+ AMIDI_DEVICE_PROTOCOL_UMP_MIDI_1_0_UP_TO_128_BITS = 3,
+
+ /**
+ * Constant representing a default protocol with Universal MIDI Packets (UMP).
+ * UMP is defined in "Universal MIDI Packet (UMP) Format and MIDI 2.0 Protocol" spec.
+ * All UMP data should be a multiple of 4 bytes.
+ * Use MIDI 1.0 through UMP with packet sizes up to 128 bits and jitter reduction timestamps.
+ */
+ AMIDI_DEVICE_PROTOCOL_UMP_MIDI_1_0_UP_TO_128_BITS_AND_JRTS = 4,
+
+ /**
+ * Constant representing a default protocol with Universal MIDI Packets (UMP).
+ * UMP is defined in "Universal MIDI Packet (UMP) Format and MIDI 2.0 Protocol" spec.
+ * All UMP data should be a multiple of 4 bytes.
+ * Use MIDI 2.0 through UMP.
+ */
+ AMIDI_DEVICE_PROTOCOL_UMP_MIDI_2_0 = 17,
+
+ /**
+ * Constant representing a default protocol with Universal MIDI Packets (UMP).
+ * UMP is defined in "Universal MIDI Packet (UMP) Format and MIDI 2.0 Protocol" spec.
+ * All UMP data should be a multiple of 4 bytes.
+ * Use MIDI 2.0 through UMP and jitter reduction timestamps.
+ */
+ AMIDI_DEVICE_PROTOCOL_UMP_MIDI_2_0_AND_JRTS = 18,
+
+ /**
+ * Constant representing a device with an unknown default protocol.
+ * If Universal MIDI Packets (UMP) are needed, use MIDI-CI through MIDI 1.0.
+ * UMP is defined in "Universal MIDI Packet (UMP) Format and MIDI 2.0 Protocol" spec.
+ * MIDI-CI is defined in "MIDI Capability Inquiry (MIDI-CI)" spec.
+ */
+ AMIDI_DEVICE_PROTOCOL_UNKNOWN = -1
+};
+
+/*
* Device API
*/
/**
@@ -134,6 +206,30 @@ ssize_t AMIDI_API AMidiDevice_getNumInputPorts(const AMidiDevice *device) __INTR
*/
ssize_t AMIDI_API AMidiDevice_getNumOutputPorts(const AMidiDevice *device) __INTRODUCED_IN(29);
+/**
+ * Gets the MIDI device default protocol.
+ *
+ * @param device Specifies the MIDI device.
+ *
+ * @return The identifier of the MIDI device default protocol:
+ * AMIDI_DEVICE_PROTOCOL_UMP_USE_MIDI_CI
+ * AMIDI_DEVICE_PROTOCOL_UMP_MIDI_1_0_UP_TO_64_BITS
+ * AMIDI_DEVICE_PROTOCOL_UMP_MIDI_1_0_UP_TO_64_BITS_AND_JRTS
+ * AMIDI_DEVICE_PROTOCOL_UMP_MIDI_1_0_UP_TO_128_BITS
+ * AMIDI_DEVICE_PROTOCOL_UMP_MIDI_1_0_UP_TO_128_BITS_AND_JRTS
+ * AMIDI_DEVICE_PROTOCOL_UMP_MIDI_2_0
+ * AMIDI_DEVICE_PROTOCOL_UMP_MIDI_2_0_AND_JRTS
+ * AMIDI_DEVICE_PROTOCOL_UNKNOWN
+ *
+ * Most devices should return PROTOCOL_UNKNOWN (-1). This is intentional as devices
+ * with default UMP support are not backwards compatible. When the device is null,
+ * return AMIDI_DEVICE_PROTOCOL_UNKNOWN.
+ *
+ * Available since API 33.
+ */
+AMidiDevice_Protocol AMIDI_API AMidiDevice_getDefaultProtocol(const AMidiDevice *device)
+ __INTRODUCED_IN(33);
+
/*
* API for receiving data from the Output port of a device.
*/
diff --git a/media/native/midi/libamidi.map.txt b/media/native/midi/libamidi.map.txt
index 62627f8c5ef7..f25f97770b50 100644
--- a/media/native/midi/libamidi.map.txt
+++ b/media/native/midi/libamidi.map.txt
@@ -2,6 +2,7 @@ LIBAMIDI {
global:
AMidiDevice_fromJava;
AMidiDevice_release;
+ AMidiDevice_getDefaultProtocol; # introduced=Tiramisu
AMidiDevice_getType;
AMidiDevice_getNumInputPorts;
AMidiDevice_getNumOutputPorts;
diff --git a/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java b/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java
index 62c313ace306..4c3b68958c0f 100644
--- a/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java
+++ b/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java
@@ -24,6 +24,7 @@ import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
+import android.media.midi.MidiDevice;
import android.media.midi.MidiDeviceInfo;
import android.media.midi.MidiDeviceServer;
import android.media.midi.MidiDeviceStatus;
@@ -63,6 +64,7 @@ public final class BluetoothMidiDevice {
"00002902-0000-1000-8000-00805f9b34fb");
private final BluetoothDevice mBluetoothDevice;
+ private final Context mContext;
private final BluetoothMidiService mService;
private final MidiManager mMidiManager;
private MidiReceiver mOutputReceiver;
@@ -136,6 +138,8 @@ public final class BluetoothMidiDevice {
// switch to receiving notifications
mBluetoothGatt.readCharacteristic(characteristic);
}
+
+ openBluetoothDevice(mBluetoothDevice);
}
} else {
Log.e(TAG, "onServicesDiscovered received: " + status);
@@ -249,6 +253,7 @@ public final class BluetoothMidiDevice {
mBluetoothGatt = mBluetoothDevice.connectGatt(context, false, mGattCallback);
+ mContext = context;
mMidiManager = (MidiManager)context.getSystemService(Context.MIDI_SERVICE);
Bundle properties = new Bundle();
@@ -260,7 +265,8 @@ public final class BluetoothMidiDevice {
inputPortReceivers[0] = mEventScheduler.getReceiver();
mDeviceServer = mMidiManager.createDeviceServer(inputPortReceivers, 1,
- null, null, properties, MidiDeviceInfo.TYPE_BLUETOOTH, mDeviceServerCallback);
+ null, null, properties, MidiDeviceInfo.TYPE_BLUETOOTH,
+ MidiDeviceInfo.PROTOCOL_UNKNOWN, mDeviceServerCallback);
mOutputReceiver = mDeviceServer.getOutputPortReceivers()[0];
@@ -309,6 +315,18 @@ public final class BluetoothMidiDevice {
}
}
+ void openBluetoothDevice(BluetoothDevice btDevice) {
+ Log.d(TAG, "openBluetoothDevice() device: " + btDevice);
+
+ MidiManager midiManager = mContext.getSystemService(MidiManager.class);
+ midiManager.openBluetoothDevice(btDevice,
+ new MidiManager.OnDeviceOpenedListener() {
+ @Override
+ public void onDeviceOpened(MidiDevice device) {
+ }
+ }, null);
+ }
+
public IBinder getBinder() {
return mDeviceServer.asBinder();
}
diff --git a/native/android/choreographer.cpp b/native/android/choreographer.cpp
index fbd4b2ec8736..e22580d6d71a 100644
--- a/native/android/choreographer.cpp
+++ b/native/android/choreographer.cpp
@@ -67,7 +67,7 @@ size_t AChoreographerFrameCallbackData_getPreferredFrameTimelineIndex(
const AChoreographerFrameCallbackData* data) {
return AChoreographerFrameCallbackData_routeGetPreferredFrameTimelineIndex(data);
}
-int64_t AChoreographerFrameCallbackData_getFrameTimelineVsyncId(
+AVsyncId AChoreographerFrameCallbackData_getFrameTimelineVsyncId(
const AChoreographerFrameCallbackData* data, size_t index) {
return AChoreographerFrameCallbackData_routeGetFrameTimelineVsyncId(data, index);
}
diff --git a/native/android/surface_control.cpp b/native/android/surface_control.cpp
index 5b19102334d9..d01a30e52749 100644
--- a/native/android/surface_control.cpp
+++ b/native/android/surface_control.cpp
@@ -661,7 +661,7 @@ void ASurfaceTransaction_setOnCommit(ASurfaceTransaction* aSurfaceTransaction, v
}
void ASurfaceTransaction_setFrameTimeline(ASurfaceTransaction* aSurfaceTransaction,
- int64_t vsyncId) {
+ AVsyncId vsyncId) {
CHECK_NOT_NULL(aSurfaceTransaction);
// TODO(b/210043506): Get start time from platform.
ASurfaceTransaction_to_Transaction(aSurfaceTransaction)
diff --git a/packages/CompanionDeviceManager/res/layout/data_transfer_confirmation.xml b/packages/CompanionDeviceManager/res/layout/data_transfer_confirmation.xml
new file mode 100644
index 000000000000..a1855fdda78d
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/layout/data_transfer_confirmation.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/activity_confirmation"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@drawable/dialog_background"
+ android:elevation="16dp"
+ android:maxHeight="400dp"
+ android:orientation="vertical"
+ android:padding="18dp"
+ android:layout_gravity="center">
+
+ <!-- Do NOT change the ID of the root LinearLayout above: it's referenced in CTS tests. -->
+
+ <TextView
+ android:id="@+id/title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:paddingHorizontal="12dp"
+ style="@*android:style/TextAppearance.Widget.Toolbar.Title"/>
+
+ <TextView
+ android:id="@+id/summary"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="12dp"
+ android:layout_marginBottom="12dp"
+ android:gravity="center"
+ android:textColor="?android:attr/textColorSecondary"
+ android:textSize="14sp" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:gravity="end">
+
+ <!-- Do NOT change the IDs of the buttons: they are referenced in CTS tests. -->
+
+ <Button
+ android:id="@+id/btn_negative"
+ style="@android:style/Widget.Material.Button.Borderless.Colored"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/consent_no"
+ android:textColor="?android:attr/textColorSecondary" />
+
+ <Button
+ android:id="@+id/btn_positive"
+ style="@android:style/Widget.Material.Button.Borderless.Colored"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/consent_yes" />
+
+ </LinearLayout>
+
+</LinearLayout> \ No newline at end of file
diff --git a/packages/CompanionDeviceManager/res/values/strings.xml b/packages/CompanionDeviceManager/res/values/strings.xml
index cb8b616ec009..25ec96065647 100644
--- a/packages/CompanionDeviceManager/res/values/strings.xml
+++ b/packages/CompanionDeviceManager/res/values/strings.xml
@@ -73,4 +73,15 @@
<!-- Negative button for the device-app association consent dialog [CHAR LIMIT=30] -->
<string name="consent_no">Don\u2019t allow</string>
+
+ <!-- ================== System data transfer ==================== -->
+ <!-- Title of the permission sync confirmation dialog. [CHAR LIMIT=60] -->
+ <string name="permission_sync_confirmation_title">Transfer app permissions to your
+ watch</string>
+
+ <!-- Text of the permission sync explanation in the confirmation dialog. [CHAR LIMIT=400] -->
+ <string name="permission_sync_summary">To make it easier to set up your watch,
+ apps installed on your watch during setup will use the same permissions as your phone.\n\n
+ These permissions may include access to your watch\u2019s microphone and location.</string>
+
</resources>
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDataTransferActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDataTransferActivity.java
new file mode 100644
index 000000000000..67efa03b645f
--- /dev/null
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDataTransferActivity.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.companiondevicemanager;
+
+import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
+
+import static java.util.Objects.requireNonNull;
+
+import android.app.Activity;
+import android.companion.SystemDataTransferRequest;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.ResultReceiver;
+import android.text.Html;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.ListView;
+import android.widget.TextView;
+
+/**
+ * This activity manages the UI of companion device data transfer.
+ */
+public class CompanionDeviceDataTransferActivity extends Activity {
+
+ private static final String LOG_TAG = CompanionDeviceDataTransferActivity.class.getSimpleName();
+
+ // UI -> SystemDataTransferProcessor
+ private static final int RESULT_CODE_SYSTEM_DATA_TRANSFER_ALLOWED = 0;
+ private static final int RESULT_CODE_SYSTEM_DATA_TRANSFER_DISALLOWED = 1;
+ private static final String EXTRA_SYSTEM_DATA_TRANSFER_REQUEST = "system_data_transfer_request";
+ private static final String EXTRA_SYSTEM_DATA_TRANSFER_RESULT_RECEIVER =
+ "system_data_transfer_result_receiver";
+
+ private SystemDataTransferRequest mRequest;
+ private ResultReceiver mCdmServiceReceiver;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ Log.i(LOG_TAG, "Creating UI for data transfer confirmation.");
+
+ getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
+
+ setContentView(R.layout.data_transfer_confirmation);
+
+ TextView titleView = findViewById(R.id.title);
+ TextView summaryView = findViewById(R.id.summary);
+ ListView listView = findViewById(R.id.device_list);
+ listView.setVisibility(View.GONE);
+ Button allowButton = findViewById(R.id.btn_positive);
+ Button disallowButton = findViewById(R.id.btn_negative);
+
+ final Intent intent = getIntent();
+ mRequest = intent.getParcelableExtra(EXTRA_SYSTEM_DATA_TRANSFER_REQUEST);
+ mCdmServiceReceiver = intent.getParcelableExtra(EXTRA_SYSTEM_DATA_TRANSFER_RESULT_RECEIVER);
+
+ requireNonNull(mRequest);
+ requireNonNull(mCdmServiceReceiver);
+
+ if (mRequest.isPermissionSyncAllPackages()
+ || !mRequest.getPermissionSyncPackages().isEmpty()) {
+ titleView.setText(Html.fromHtml(getString(
+ R.string.permission_sync_confirmation_title), 0));
+ summaryView.setText(getString(R.string.permission_sync_summary));
+ allowButton.setOnClickListener(v -> allow());
+ disallowButton.setOnClickListener(v -> disallow());
+ }
+ }
+
+ private void allow() {
+ Log.i(LOG_TAG, "allow()");
+
+ sendDataToReceiver(RESULT_CODE_SYSTEM_DATA_TRANSFER_ALLOWED);
+
+ setResultAndFinish(RESULT_CODE_SYSTEM_DATA_TRANSFER_ALLOWED);
+ }
+
+ private void disallow() {
+ Log.i(LOG_TAG, "disallow()");
+
+ sendDataToReceiver(RESULT_CODE_SYSTEM_DATA_TRANSFER_DISALLOWED);
+
+ setResultAndFinish(RESULT_CODE_SYSTEM_DATA_TRANSFER_DISALLOWED);
+ }
+
+ private void sendDataToReceiver(int cdmResultCode) {
+ Bundle data = new Bundle();
+ data.putParcelable(EXTRA_SYSTEM_DATA_TRANSFER_REQUEST, mRequest);
+ mCdmServiceReceiver.send(cdmResultCode, data);
+ }
+
+ private void setResultAndFinish(int cdmResultCode) {
+ setResult(cdmResultCode == RESULT_CODE_SYSTEM_DATA_TRANSFER_ALLOWED
+ ? RESULT_OK : RESULT_CANCELED);
+ finish();
+ }
+}
diff --git a/packages/ConnectivityT/framework-t/Android.bp b/packages/ConnectivityT/framework-t/Android.bp
index d3d8bba16c7c..223bdcdd9c95 100644
--- a/packages/ConnectivityT/framework-t/Android.bp
+++ b/packages/ConnectivityT/framework-t/Android.bp
@@ -129,6 +129,11 @@ filegroup {
"src/android/net/EthernetNetworkSpecifier.java",
"src/android/net/IEthernetManager.aidl",
"src/android/net/IEthernetServiceListener.aidl",
+ "src/android/net/IInternalNetworkManagementListener.aidl",
+ "src/android/net/InternalNetworkUpdateRequest.java",
+ "src/android/net/InternalNetworkUpdateRequest.aidl",
+ "src/android/net/InternalNetworkManagementException.java",
+ "src/android/net/InternalNetworkManagementException.aidl",
"src/android/net/ITetheredInterfaceCallback.aidl",
],
path: "src",
diff --git a/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStats.java b/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStats.java
index d33666d744d1..2b6570a6ecb0 100644
--- a/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStats.java
+++ b/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStats.java
@@ -556,7 +556,7 @@ public final class NetworkStats implements AutoCloseable {
/**
* Collects history results for uid and resets history enumeration index.
*/
- void startHistoryEnumeration(int uid, int tag, int state) {
+ void startHistoryUidEnumeration(int uid, int tag, int state) {
mHistory = null;
try {
mHistory = mSession.getHistoryIntervalForUid(mTemplate, uid,
@@ -571,6 +571,20 @@ public final class NetworkStats implements AutoCloseable {
}
/**
+ * Collects history results for network and resets history enumeration index.
+ */
+ void startHistoryDeviceEnumeration() {
+ try {
+ mHistory = mSession.getHistoryIntervalForNetwork(
+ mTemplate, NetworkStatsHistory.FIELD_ALL, mStartTimeStamp, mEndTimeStamp);
+ } catch (RemoteException e) {
+ Log.w(TAG, e);
+ mHistory = null;
+ }
+ mEnumerationIndex = 0;
+ }
+
+ /**
* Starts uid enumeration for current user.
* @throws RemoteException
*/
diff --git a/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java b/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java
index f74edb1a01e5..8813f984519b 100644
--- a/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java
+++ b/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java
@@ -17,7 +17,10 @@
package android.app.usage;
import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -54,7 +57,6 @@ import com.android.net.module.util.NetworkIdentityUtils;
import java.util.List;
import java.util.Objects;
-import java.util.Set;
/**
* Provides access to network usage history and statistics. Usage data is collected in
@@ -124,6 +126,19 @@ public class NetworkStatsManager {
private final Context mContext;
private final INetworkStatsService mService;
+ /**
+ * Type constants for reading different types of Data Usage.
+ * @hide
+ */
+ // @SystemApi(client = MODULE_LIBRARIES)
+ public static final String PREFIX_DEV = "dev";
+ /** @hide */
+ public static final String PREFIX_XT = "xt";
+ /** @hide */
+ public static final String PREFIX_UID = "uid";
+ /** @hide */
+ public static final String PREFIX_UID_TAG = "uid_tag";
+
/** @hide */
public static final int FLAG_POLL_ON_OPEN = 1 << 0;
/** @hide */
@@ -142,6 +157,11 @@ public class NetworkStatsManager {
setAugmentWithSubscriptionPlan(true);
}
+ /** @hide */
+ public INetworkStatsService getBinder() {
+ return mService;
+ }
+
/**
* Set poll on open flag to indicate the poll is needed before service gets statistics
* result. This is default enabled. However, for any non-privileged caller, the poll might
@@ -150,7 +170,13 @@ public class NetworkStatsManager {
* @param pollOnOpen true if poll is needed.
* @hide
*/
- // @SystemApi(client = MODULE_LIBRARIES)
+ // The system will ignore any non-default values for non-privileged
+ // processes, so processes that don't hold the appropriate permissions
+ // can make no use of this API.
+ @SystemApi(client = MODULE_LIBRARIES)
+ @RequiresPermission(anyOf = {
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ android.Manifest.permission.NETWORK_STACK})
public void setPollOnOpen(boolean pollOnOpen) {
if (pollOnOpen) {
mFlags |= FLAG_POLL_ON_OPEN;
@@ -204,9 +230,10 @@ public class NetworkStatsManager {
*/
@NonNull
@WorkerThread
- // @SystemApi(client = MODULE_LIBRARIES)
+ @SystemApi(client = MODULE_LIBRARIES)
public Bucket querySummaryForDevice(@NonNull NetworkTemplate template,
long startTime, long endTime) {
+ Objects.requireNonNull(template);
try {
NetworkStats stats =
new NetworkStats(mContext, template, mFlags, startTime, endTime, mService);
@@ -378,10 +405,11 @@ public class NetworkStatsManager {
* @hide
*/
@NonNull
- // @SystemApi(client = MODULE_LIBRARIES)
+ @SystemApi(client = MODULE_LIBRARIES)
@WorkerThread
public NetworkStats querySummary(@NonNull NetworkTemplate template, long startTime,
long endTime) throws SecurityException {
+ Objects.requireNonNull(template);
try {
NetworkStats result =
new NetworkStats(mContext, template, mFlags, startTime, endTime, mService);
@@ -411,10 +439,11 @@ public class NetworkStatsManager {
* @hide
*/
@NonNull
- // @SystemApi(client = MODULE_LIBRARIES)
+ @SystemApi(client = MODULE_LIBRARIES)
@WorkerThread
public NetworkStats queryTaggedSummary(@NonNull NetworkTemplate template, long startTime,
long endTime) throws SecurityException {
+ Objects.requireNonNull(template);
try {
NetworkStats result =
new NetworkStats(mContext, template, mFlags, startTime, endTime, mService);
@@ -427,6 +456,43 @@ public class NetworkStatsManager {
}
/**
+ * Query usage statistics details for networks matching a given {@link NetworkTemplate}.
+ *
+ * Result is not aggregated over time. This means buckets' start and
+ * end timestamps will be between 'startTime' and 'endTime' parameters.
+ * <p>Only includes buckets whose entire time period is included between
+ * startTime and endTime. Doesn't interpolate or return partial buckets.
+ * Since bucket length is in the order of hours, this
+ * method cannot be used to measure data usage on a fine grained time scale.
+ * This may take a long time, and apps should avoid calling this on their main thread.
+ *
+ * @param template Template used to match networks. See {@link NetworkTemplate}.
+ * @param startTime Start of period, in milliseconds since the Unix epoch, see
+ * {@link java.lang.System#currentTimeMillis}.
+ * @param endTime End of period, in milliseconds since the Unix epoch, see
+ * {@link java.lang.System#currentTimeMillis}.
+ * @return Statistics which is described above.
+ * @hide
+ */
+ @NonNull
+ @SystemApi(client = MODULE_LIBRARIES)
+ @WorkerThread
+ public NetworkStats queryDetailsForDevice(@NonNull NetworkTemplate template,
+ long startTime, long endTime) {
+ Objects.requireNonNull(template);
+ try {
+ final NetworkStats result =
+ new NetworkStats(mContext, template, mFlags, startTime, endTime, mService);
+ result.startHistoryDeviceEnumeration();
+ return result;
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+
+ return null; // To make the compiler happy.
+ }
+
+ /**
* Query network usage statistics details for a given uid.
* This may take a long time, and apps should avoid calling this on their main thread.
*
@@ -492,7 +558,8 @@ public class NetworkStatsManager {
* @param endTime End of period. Defined in terms of "Unix time", see
* {@link java.lang.System#currentTimeMillis}.
* @param uid UID of app
- * @param tag TAG of interest. Use {@link NetworkStats.Bucket#TAG_NONE} for no tags.
+ * @param tag TAG of interest. Use {@link NetworkStats.Bucket#TAG_NONE} for aggregated data
+ * across all the tags.
* @param state state of interest. Use {@link NetworkStats.Bucket#STATE_ALL} to aggregate
* traffic from all states.
* @return Statistics object or null if an error happened during statistics collection.
@@ -507,21 +574,52 @@ public class NetworkStatsManager {
return queryDetailsForUidTagState(template, startTime, endTime, uid, tag, state);
}
- /** @hide */
- public NetworkStats queryDetailsForUidTagState(NetworkTemplate template,
+ /**
+ * Query network usage statistics details for a given template, uid, tag, and state.
+ *
+ * Only usable for uids belonging to calling user. Result is not aggregated over time.
+ * This means buckets' start and end timestamps are going to be between 'startTime' and
+ * 'endTime' parameters. The uid is going to be the same as the 'uid' parameter, the tag
+ * the same as the 'tag' parameter, and the state the same as the 'state' parameter.
+ * defaultNetwork is going to be {@link NetworkStats.Bucket#DEFAULT_NETWORK_ALL},
+ * metered is going to be {@link NetworkStats.Bucket#METERED_ALL}, and
+ * roaming is going to be {@link NetworkStats.Bucket#ROAMING_ALL}.
+ * <p>Only includes buckets that atomically occur in the inclusive time range. Doesn't
+ * interpolate across partial buckets. Since bucket length is in the order of hours, this
+ * method cannot be used to measure data usage on a fine grained time scale.
+ * This may take a long time, and apps should avoid calling this on their main thread.
+ *
+ * @param template Template used to match networks. See {@link NetworkTemplate}.
+ * @param startTime Start of period, in milliseconds since the Unix epoch, see
+ * {@link java.lang.System#currentTimeMillis}.
+ * @param endTime End of period, in milliseconds since the Unix epoch, see
+ * {@link java.lang.System#currentTimeMillis}.
+ * @param uid UID of app
+ * @param tag TAG of interest. Use {@link NetworkStats.Bucket#TAG_NONE} for aggregated data
+ * across all the tags.
+ * @param state state of interest. Use {@link NetworkStats.Bucket#STATE_ALL} to aggregate
+ * traffic from all states.
+ * @return Statistics which is described above.
+ * @hide
+ */
+ @NonNull
+ @SystemApi(client = MODULE_LIBRARIES)
+ @WorkerThread
+ public NetworkStats queryDetailsForUidTagState(@NonNull NetworkTemplate template,
long startTime, long endTime, int uid, int tag, int state) throws SecurityException {
-
- NetworkStats result;
+ Objects.requireNonNull(template);
try {
- result = new NetworkStats(mContext, template, mFlags, startTime, endTime, mService);
- result.startHistoryEnumeration(uid, tag, state);
+ final NetworkStats result = new NetworkStats(
+ mContext, template, mFlags, startTime, endTime, mService);
+ result.startHistoryUidEnumeration(uid, tag, state);
+ return result;
} catch (RemoteException e) {
Log.e(TAG, "Error while querying stats for uid=" + uid + " tag=" + tag
+ " state=" + state, e);
- return null;
+ e.rethrowFromSystemServer();
}
- return result;
+ return null; // To make the compiler happy.
}
/**
@@ -578,26 +676,49 @@ public class NetworkStatsManager {
}
/**
- * Query realtime network usage statistics details with interfaces constrains.
- * Return snapshot of current UID statistics, including any {@link TrafficStats#UID_TETHERING},
- * video calling data usage and count of network operations that set by
- * {@link TrafficStats#incrementOperationCount}. The returned data doesn't include any
- * statistics that is reported by {@link NetworkStatsProvider}.
+ * Query realtime mobile network usage statistics.
+ *
+ * Return a snapshot of current UID network statistics, as it applies
+ * to the mobile radios of the device. The snapshot will include any
+ * tethering traffic, video calling data usage and count of
+ * network operations set by {@link TrafficStats#incrementOperationCount}
+ * made over a mobile radio.
+ * The snapshot will not include any statistics that cannot be seen by
+ * the kernel, e.g. statistics reported by {@link NetworkStatsProvider}s.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK)
+ @NonNull public android.net.NetworkStats getMobileUidStats() {
+ try {
+ return mService.getUidStatsForTransport(TRANSPORT_CELLULAR);
+ } catch (RemoteException e) {
+ if (DBG) Log.d(TAG, "Remote exception when get Mobile uid stats");
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Query realtime Wi-Fi network usage statistics.
*
- * @param requiredIfaces A list of interfaces the stats should be restricted to, or
- * {@link NetworkStats#INTERFACES_ALL}.
+ * Return a snapshot of current UID network statistics, as it applies
+ * to the Wi-Fi radios of the device. The snapshot will include any
+ * tethering traffic, video calling data usage and count of
+ * network operations set by {@link TrafficStats#incrementOperationCount}
+ * made over a Wi-Fi radio.
+ * The snapshot will not include any statistics that cannot be seen by
+ * the kernel, e.g. statistics reported by {@link NetworkStatsProvider}s.
*
* @hide
*/
- //@SystemApi
+ @SystemApi
@RequiresPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK)
- @NonNull public android.net.NetworkStats getDetailedUidStats(
- @NonNull Set<String> requiredIfaces) {
- Objects.requireNonNull(requiredIfaces, "requiredIfaces cannot be null");
+ @NonNull public android.net.NetworkStats getWifiUidStats() {
try {
- return mService.getDetailedUidStats(requiredIfaces.toArray(new String[0]));
+ return mService.getUidStatsForTransport(TRANSPORT_WIFI);
} catch (RemoteException e) {
- if (DBG) Log.d(TAG, "Remote exception when get detailed uid stats");
+ if (DBG) Log.d(TAG, "Remote exception when get WiFi uid stats");
throw e.rethrowFromSystemServer();
}
}
@@ -877,7 +998,7 @@ public class NetworkStatsManager {
*
* @hide
*/
- // @SystemApi
+ @SystemApi(client = MODULE_LIBRARIES)
@RequiresPermission(anyOf = {
NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
android.Manifest.permission.NETWORK_STACK})
@@ -890,17 +1011,18 @@ public class NetworkStatsManager {
}
/**
- * Advise persistence threshold; may be overridden internally.
+ * Set default value of global alert bytes, the value will be clamped to [128kB, 2MB].
*
* @hide
*/
- // @SystemApi
+ @SystemApi(client = MODULE_LIBRARIES)
@RequiresPermission(anyOf = {
NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
- android.Manifest.permission.NETWORK_STACK})
- public void advisePersistThreshold(long thresholdBytes) {
+ Manifest.permission.NETWORK_STACK})
+ public void setDefaultGlobalAlert(long alertBytes) {
try {
- mService.advisePersistThreshold(thresholdBytes);
+ // TODO: Sync internal naming with the API surface.
+ mService.advisePersistThreshold(alertBytes);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -911,7 +1033,7 @@ public class NetworkStatsManager {
*
* @hide
*/
- // @SystemApi
+ @SystemApi(client = MODULE_LIBRARIES)
@RequiresPermission(anyOf = {
NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
android.Manifest.permission.NETWORK_STACK})
@@ -927,9 +1049,17 @@ public class NetworkStatsManager {
* Set the warning and limit to all registered custom network stats providers.
* Note that invocation of any interface will be sent to all providers.
*
+ * Asynchronicity notes : because traffic may be happening on the device at the same time, it
+ * doesn't make sense to wait for the warning and limit to be set – a caller still wouldn't
+ * know when exactly it was effective. All that can matter is that it's done quickly. Also,
+ * this method can't fail, so there is no status to return. All providers will see the new
+ * values soon.
+ * As such, this method returns immediately and sends the warning and limit to all providers
+ * as soon as possible through a one-way binder call.
+ *
* @hide
*/
- // @SystemApi
+ @SystemApi(client = MODULE_LIBRARIES)
@RequiresPermission(anyOf = {
NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
android.Manifest.permission.NETWORK_STACK})
diff --git a/packages/ConnectivityT/framework-t/src/android/net/DataUsageRequest.java b/packages/ConnectivityT/framework-t/src/android/net/DataUsageRequest.java
index b06d515b3acf..f0ff46522d15 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/DataUsageRequest.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/DataUsageRequest.java
@@ -75,7 +75,7 @@ public final class DataUsageRequest implements Parcelable {
@Override
public DataUsageRequest createFromParcel(Parcel in) {
int requestId = in.readInt();
- NetworkTemplate template = in.readParcelable(null);
+ NetworkTemplate template = in.readParcelable(null, android.net.NetworkTemplate.class);
long thresholdInBytes = in.readLong();
DataUsageRequest result = new DataUsageRequest(requestId, template,
thresholdInBytes);
diff --git a/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkSpecifier.java b/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkSpecifier.java
index 62c576144221..925d12b574a6 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkSpecifier.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkSpecifier.java
@@ -23,8 +23,6 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
-import com.android.internal.util.Preconditions;
-
import java.util.Objects;
/**
@@ -47,7 +45,9 @@ public final class EthernetNetworkSpecifier extends NetworkSpecifier implements
* @param interfaceName Name of the ethernet interface the specifier refers to.
*/
public EthernetNetworkSpecifier(@NonNull String interfaceName) {
- Preconditions.checkStringNotEmpty(interfaceName);
+ if (TextUtils.isEmpty(interfaceName)) {
+ throw new IllegalArgumentException();
+ }
mInterfaceName = interfaceName;
}
diff --git a/core/java/android/net/IInternalNetworkManagementListener.aidl b/packages/ConnectivityT/framework-t/src/android/net/IInternalNetworkManagementListener.aidl
index 69cde3bd14e8..69cde3bd14e8 100644
--- a/core/java/android/net/IInternalNetworkManagementListener.aidl
+++ b/packages/ConnectivityT/framework-t/src/android/net/IInternalNetworkManagementListener.aidl
diff --git a/packages/ConnectivityT/framework-t/src/android/net/INetworkStatsService.aidl b/packages/ConnectivityT/framework-t/src/android/net/INetworkStatsService.aidl
index a4babb543dbd..da0aa99b9370 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/INetworkStatsService.aidl
+++ b/packages/ConnectivityT/framework-t/src/android/net/INetworkStatsService.aidl
@@ -49,14 +49,8 @@ interface INetworkStatsService {
@UnsupportedAppUsage
NetworkStats getDataLayerSnapshotForUid(int uid);
- /** Get a detailed snapshot of stats since boot for all UIDs.
- *
- * <p>Results will not always be limited to stats on requiredIfaces when specified: stats for
- * interfaces stacked on the specified interfaces, or for interfaces on which the specified
- * interfaces are stacked on, will also be included.
- * @param requiredIfaces Interface names to get data for, or {@link NetworkStats#INTERFACES_ALL}.
- */
- NetworkStats getDetailedUidStats(in String[] requiredIfaces);
+ /** Get the transport NetworkStats for all UIDs since boot. */
+ NetworkStats getUidStatsForTransport(int transport);
/** Return set of any ifaces associated with mobile networks since boot. */
@UnsupportedAppUsage
diff --git a/packages/ConnectivityT/framework-t/src/android/net/INetworkStatsSession.aidl b/packages/ConnectivityT/framework-t/src/android/net/INetworkStatsSession.aidl
index babe0bfb9760..ab70be826f8e 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/INetworkStatsSession.aidl
+++ b/packages/ConnectivityT/framework-t/src/android/net/INetworkStatsSession.aidl
@@ -32,6 +32,11 @@ interface INetworkStatsSession {
/** Return historical network layer stats for traffic that matches template. */
@UnsupportedAppUsage
NetworkStatsHistory getHistoryForNetwork(in NetworkTemplate template, int fields);
+ /**
+ * Return historical network layer stats for traffic that matches template, start and end
+ * timestamp.
+ */
+ NetworkStatsHistory getHistoryIntervalForNetwork(in NetworkTemplate template, int fields, long start, long end);
/**
* Return network layer usage summary per UID for traffic that matches template.
diff --git a/core/java/android/net/InternalNetworkManagementException.aidl b/packages/ConnectivityT/framework-t/src/android/net/InternalNetworkManagementException.aidl
index dcce706989f6..dcce706989f6 100644
--- a/core/java/android/net/InternalNetworkManagementException.aidl
+++ b/packages/ConnectivityT/framework-t/src/android/net/InternalNetworkManagementException.aidl
diff --git a/core/java/android/net/InternalNetworkManagementException.java b/packages/ConnectivityT/framework-t/src/android/net/InternalNetworkManagementException.java
index 7f4e403f2259..7f4e403f2259 100644
--- a/core/java/android/net/InternalNetworkManagementException.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/InternalNetworkManagementException.java
diff --git a/core/java/android/net/InternalNetworkUpdateRequest.aidl b/packages/ConnectivityT/framework-t/src/android/net/InternalNetworkUpdateRequest.aidl
index da00cb97afb4..da00cb97afb4 100644
--- a/core/java/android/net/InternalNetworkUpdateRequest.aidl
+++ b/packages/ConnectivityT/framework-t/src/android/net/InternalNetworkUpdateRequest.aidl
diff --git a/core/java/android/net/InternalNetworkUpdateRequest.java b/packages/ConnectivityT/framework-t/src/android/net/InternalNetworkUpdateRequest.java
index f42c4b7c420d..f42c4b7c420d 100644
--- a/core/java/android/net/InternalNetworkUpdateRequest.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/InternalNetworkUpdateRequest.java
diff --git a/packages/ConnectivityT/framework-t/src/android/net/IpSecConfig.java b/packages/ConnectivityT/framework-t/src/android/net/IpSecConfig.java
index 575c5ed968f8..03bb187f119f 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/IpSecConfig.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/IpSecConfig.java
@@ -267,14 +267,14 @@ public final class IpSecConfig implements Parcelable {
mMode = in.readInt();
mSourceAddress = in.readString();
mDestinationAddress = in.readString();
- mNetwork = (Network) in.readParcelable(Network.class.getClassLoader());
+ mNetwork = (Network) in.readParcelable(Network.class.getClassLoader(), android.net.Network.class);
mSpiResourceId = in.readInt();
mEncryption =
- (IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
+ (IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader(), android.net.IpSecAlgorithm.class);
mAuthentication =
- (IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
+ (IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader(), android.net.IpSecAlgorithm.class);
mAuthenticatedEncryption =
- (IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
+ (IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader(), android.net.IpSecAlgorithm.class);
mEncapType = in.readInt();
mEncapSocketResourceId = in.readInt();
mEncapRemotePort = in.readInt();
diff --git a/packages/ConnectivityT/framework-t/src/android/net/IpSecManager.java b/packages/ConnectivityT/framework-t/src/android/net/IpSecManager.java
index 49aa99b0975b..a423783bc1ca 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/IpSecManager.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/IpSecManager.java
@@ -27,6 +27,7 @@ import android.annotation.TestApi;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Binder;
+import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
@@ -988,6 +989,29 @@ public final class IpSecManager {
}
/**
+ * @hide
+ */
+ public IpSecTransformResponse createTransform(IpSecConfig config, IBinder binder,
+ String callingPackage) {
+ try {
+ return mService.createTransform(config, binder, callingPackage);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public void deleteTransform(int resourceId) {
+ try {
+ mService.deleteTransform(resourceId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Construct an instance of IpSecManager within an application context.
*
* @param context the application context for this manager
diff --git a/packages/ConnectivityT/framework-t/src/android/net/IpSecTransform.java b/packages/ConnectivityT/framework-t/src/android/net/IpSecTransform.java
index 36199a046cfc..68ae5de4ee70 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/IpSecTransform.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/IpSecTransform.java
@@ -26,9 +26,6 @@ import android.annotation.SystemApi;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Binder;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.ServiceSpecificException;
import android.util.Log;
@@ -93,16 +90,9 @@ public final class IpSecTransform implements AutoCloseable {
mResourceId = INVALID_RESOURCE_ID;
}
- private IIpSecService getIpSecService() {
- IBinder b = ServiceManager.getService(android.content.Context.IPSEC_SERVICE);
- if (b == null) {
- throw new RemoteException("Failed to connect to IpSecService")
- .rethrowAsRuntimeException();
- }
-
- return IIpSecService.Stub.asInterface(b);
+ private IpSecManager getIpSecManager(Context context) {
+ return context.getSystemService(IpSecManager.class);
}
-
/**
* Checks the result status and throws an appropriate exception if the status is not Status.OK.
*/
@@ -130,8 +120,7 @@ public final class IpSecTransform implements AutoCloseable {
IpSecManager.SpiUnavailableException {
synchronized (this) {
try {
- IIpSecService svc = getIpSecService();
- IpSecTransformResponse result = svc.createTransform(
+ IpSecTransformResponse result = getIpSecManager(mContext).createTransform(
mConfig, new Binder(), mContext.getOpPackageName());
int status = result.status;
checkResultStatus(status);
@@ -140,8 +129,6 @@ public final class IpSecTransform implements AutoCloseable {
mCloseGuard.open("build");
} catch (ServiceSpecificException e) {
throw IpSecManager.rethrowUncheckedExceptionFromServiceSpecificException(e);
- } catch (RemoteException e) {
- throw e.rethrowAsRuntimeException();
}
}
@@ -177,10 +164,7 @@ public final class IpSecTransform implements AutoCloseable {
return;
}
try {
- IIpSecService svc = getIpSecService();
- svc.deleteTransform(mResourceId);
- } catch (RemoteException e) {
- throw e.rethrowAsRuntimeException();
+ getIpSecManager(mContext).deleteTransform(mResourceId);
} catch (Exception e) {
// On close we swallow all random exceptions since failure to close is not
// actionable by the user.
diff --git a/packages/ConnectivityT/framework-t/src/android/net/IpSecUdpEncapResponse.java b/packages/ConnectivityT/framework-t/src/android/net/IpSecUdpEncapResponse.java
index 732cf198a9cc..390af8236696 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/IpSecUdpEncapResponse.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/IpSecUdpEncapResponse.java
@@ -81,7 +81,7 @@ public final class IpSecUdpEncapResponse implements Parcelable {
status = in.readInt();
resourceId = in.readInt();
port = in.readInt();
- fileDescriptor = in.readParcelable(ParcelFileDescriptor.class.getClassLoader());
+ fileDescriptor = in.readParcelable(ParcelFileDescriptor.class.getClassLoader(), android.os.ParcelFileDescriptor.class);
}
@android.annotation.NonNull
diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentity.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentity.java
index 8f1115e065dd..d3d5a087ccac 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentity.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentity.java
@@ -16,18 +16,29 @@
package android.net;
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.net.ConnectivityManager.TYPE_WIFI;
+import static android.net.NetworkTemplate.NETWORK_TYPE_ALL;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
import android.content.Context;
import android.net.wifi.WifiInfo;
import android.service.NetworkIdentityProto;
-import android.telephony.Annotation.NetworkType;
+import android.telephony.Annotation;
+import android.telephony.TelephonyManager;
import android.util.proto.ProtoOutputStream;
+import com.android.net.module.util.CollectionUtils;
import com.android.net.module.util.NetworkCapabilitiesUtils;
import com.android.net.module.util.NetworkIdentityUtils;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Objects;
@@ -37,11 +48,24 @@ import java.util.Objects;
*
* @hide
*/
-public class NetworkIdentity implements Comparable<NetworkIdentity> {
+@SystemApi(client = MODULE_LIBRARIES)
+public class NetworkIdentity {
private static final String TAG = "NetworkIdentity";
+ /** @hide */
+ // TODO: Remove this after migrating all callers to use
+ // {@link NetworkTemplate#NETWORK_TYPE_ALL} instead.
public static final int SUBTYPE_COMBINED = -1;
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "OEM_MANAGED_" }, flag = true, value = {
+ NetworkTemplate.OEM_MANAGED_NO,
+ NetworkTemplate.OEM_MANAGED_PAID,
+ NetworkTemplate.OEM_MANAGED_PRIVATE
+ })
+ public @interface OemManaged{}
+
/**
* Network has no {@code NetworkCapabilities#NET_CAPABILITY_OEM_*}.
* @hide
@@ -51,29 +75,32 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> {
* Network has {@link NetworkCapabilities#NET_CAPABILITY_OEM_PAID}.
* @hide
*/
- public static final int OEM_PAID = 0x1;
+ public static final int OEM_PAID = 1 << 0;
/**
* Network has {@link NetworkCapabilities#NET_CAPABILITY_OEM_PRIVATE}.
* @hide
*/
- public static final int OEM_PRIVATE = 0x2;
+ public static final int OEM_PRIVATE = 1 << 1;
+
+ private static final long SUPPORTED_OEM_MANAGED_TYPES = OEM_PAID | OEM_PRIVATE;
final int mType;
- final int mSubType;
+ final int mRatType;
final String mSubscriberId;
- final String mNetworkId;
+ final String mWifiNetworkKey;
final boolean mRoaming;
final boolean mMetered;
final boolean mDefaultNetwork;
final int mOemManaged;
+ /** @hide */
public NetworkIdentity(
- int type, int subType, String subscriberId, String networkId, boolean roaming,
- boolean metered, boolean defaultNetwork, int oemManaged) {
+ int type, int ratType, @Nullable String subscriberId, @Nullable String wifiNetworkKey,
+ boolean roaming, boolean metered, boolean defaultNetwork, int oemManaged) {
mType = type;
- mSubType = subType;
+ mRatType = ratType;
mSubscriberId = subscriberId;
- mNetworkId = networkId;
+ mWifiNetworkKey = wifiNetworkKey;
mRoaming = roaming;
mMetered = metered;
mDefaultNetwork = defaultNetwork;
@@ -82,7 +109,7 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> {
@Override
public int hashCode() {
- return Objects.hash(mType, mSubType, mSubscriberId, mNetworkId, mRoaming, mMetered,
+ return Objects.hash(mType, mRatType, mSubscriberId, mWifiNetworkKey, mRoaming, mMetered,
mDefaultNetwork, mOemManaged);
}
@@ -90,9 +117,9 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> {
public boolean equals(@Nullable Object obj) {
if (obj instanceof NetworkIdentity) {
final NetworkIdentity ident = (NetworkIdentity) obj;
- return mType == ident.mType && mSubType == ident.mSubType && mRoaming == ident.mRoaming
+ return mType == ident.mType && mRatType == ident.mRatType && mRoaming == ident.mRoaming
&& Objects.equals(mSubscriberId, ident.mSubscriberId)
- && Objects.equals(mNetworkId, ident.mNetworkId)
+ && Objects.equals(mWifiNetworkKey, ident.mWifiNetworkKey)
&& mMetered == ident.mMetered
&& mDefaultNetwork == ident.mDefaultNetwork
&& mOemManaged == ident.mOemManaged;
@@ -104,18 +131,18 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> {
public String toString() {
final StringBuilder builder = new StringBuilder("{");
builder.append("type=").append(mType);
- builder.append(", subType=");
- if (mSubType == SUBTYPE_COMBINED) {
+ builder.append(", ratType=");
+ if (mRatType == NETWORK_TYPE_ALL) {
builder.append("COMBINED");
} else {
- builder.append(mSubType);
+ builder.append(mRatType);
}
if (mSubscriberId != null) {
builder.append(", subscriberId=")
.append(NetworkIdentityUtils.scrubSubscriberId(mSubscriberId));
}
- if (mNetworkId != null) {
- builder.append(", networkId=").append(mNetworkId);
+ if (mWifiNetworkKey != null) {
+ builder.append(", wifiNetworkKey=").append(mWifiNetworkKey);
}
if (mRoaming) {
builder.append(", ROAMING");
@@ -153,18 +180,14 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> {
}
}
+ /** @hide */
public void dumpDebug(ProtoOutputStream proto, long tag) {
final long start = proto.start(tag);
proto.write(NetworkIdentityProto.TYPE, mType);
- // Not dumping mSubType, subtypes are no longer supported.
+ // TODO: dump mRatType as well.
- if (mSubscriberId != null) {
- proto.write(NetworkIdentityProto.SUBSCRIBER_ID,
- NetworkIdentityUtils.scrubSubscriberId(mSubscriberId));
- }
- proto.write(NetworkIdentityProto.NETWORK_ID, mNetworkId);
proto.write(NetworkIdentityProto.ROAMING, mRoaming);
proto.write(NetworkIdentityProto.METERED, mMetered);
proto.write(NetworkIdentityProto.DEFAULT_NETWORK, mDefaultNetwork);
@@ -173,77 +196,99 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> {
proto.end(start);
}
+ /** Get the network type of this instance. */
public int getType() {
return mType;
}
- public int getSubType() {
- return mSubType;
+ /** Get the Radio Access Technology(RAT) type of this instance. */
+ public int getRatType() {
+ return mRatType;
}
+ /** Get the Subscriber Id of this instance. */
+ @Nullable
public String getSubscriberId() {
return mSubscriberId;
}
- public String getNetworkId() {
- return mNetworkId;
+ /** Get the Wifi Network Key of this instance. See {@link WifiInfo#getNetworkKey()}. */
+ @Nullable
+ public String getWifiNetworkKey() {
+ return mWifiNetworkKey;
}
+ /** @hide */
+ // TODO: Remove this function after all callers are removed.
public boolean getRoaming() {
return mRoaming;
}
+ /** Return whether this network is roaming. */
+ public boolean isRoaming() {
+ return mRoaming;
+ }
+
+ /** @hide */
+ // TODO: Remove this function after all callers are removed.
public boolean getMetered() {
return mMetered;
}
+ /** Return whether this network is metered. */
+ public boolean isMetered() {
+ return mMetered;
+ }
+
+ /** @hide */
+ // TODO: Remove this function after all callers are removed.
public boolean getDefaultNetwork() {
return mDefaultNetwork;
}
+ /** Return whether this network is the default network. */
+ public boolean isDefaultNetwork() {
+ return mDefaultNetwork;
+ }
+
+ /** Get the OEM managed type of this instance. */
public int getOemManaged() {
return mOemManaged;
}
/**
- * Build a {@link NetworkIdentity} from the given {@link NetworkStateSnapshot} and
- * {@code subType}, assuming that any mobile networks are using the current IMSI.
- * The subType if applicable, should be set as one of the TelephonyManager.NETWORK_TYPE_*
- * constants, or {@link android.telephony.TelephonyManager#NETWORK_TYPE_UNKNOWN} if not.
+ * Assemble a {@link NetworkIdentity} from the passed arguments.
+ *
+ * This methods builds an identity based on the capabilities of the network in the
+ * snapshot and other passed arguments. The identity is used as a key to record data usage.
+ *
+ * @param snapshot the snapshot of network state. See {@link NetworkStateSnapshot}.
+ * @param defaultNetwork whether the network is a default network.
+ * @param ratType the Radio Access Technology(RAT) type of the network. Or
+ * {@link TelephonyManager#NETWORK_TYPE_UNKNOWN} if not applicable.
+ * See {@code TelephonyManager.NETWORK_TYPE_*}.
+ * @hide
+ * @deprecated See {@link NetworkIdentity.Builder}.
*/
+ // TODO: Remove this after all callers are migrated to use new Api.
+ @Deprecated
+ @NonNull
public static NetworkIdentity buildNetworkIdentity(Context context,
- NetworkStateSnapshot snapshot, boolean defaultNetwork, @NetworkType int subType) {
- final int legacyType = snapshot.getLegacyType();
-
- final String subscriberId = snapshot.getSubscriberId();
- String networkId = null;
- boolean roaming = !snapshot.getNetworkCapabilities().hasCapability(
- NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING);
- boolean metered = !(snapshot.getNetworkCapabilities().hasCapability(
- NetworkCapabilities.NET_CAPABILITY_NOT_METERED)
- || snapshot.getNetworkCapabilities().hasCapability(
- NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED));
-
- final int oemManaged = getOemBitfield(snapshot.getNetworkCapabilities());
-
- if (legacyType == TYPE_WIFI) {
- final TransportInfo transportInfo = snapshot.getNetworkCapabilities()
- .getTransportInfo();
- if (transportInfo instanceof WifiInfo) {
- final WifiInfo info = (WifiInfo) transportInfo;
- networkId = info != null ? info.getCurrentNetworkKey() : null;
- }
+ @NonNull NetworkStateSnapshot snapshot,
+ boolean defaultNetwork, @Annotation.NetworkType int ratType) {
+ final NetworkIdentity.Builder builder = new NetworkIdentity.Builder()
+ .setNetworkStateSnapshot(snapshot).setDefaultNetwork(defaultNetwork);
+ if (snapshot.getLegacyType() == TYPE_MOBILE && ratType != NETWORK_TYPE_ALL) {
+ builder.setRatType(ratType);
}
-
- return new NetworkIdentity(legacyType, subType, subscriberId, networkId, roaming, metered,
- defaultNetwork, oemManaged);
+ return builder.build();
}
/**
* Builds a bitfield of {@code NetworkIdentity.OEM_*} based on {@link NetworkCapabilities}.
* @hide
*/
- public static int getOemBitfield(NetworkCapabilities nc) {
+ public static int getOemBitfield(@NonNull NetworkCapabilities nc) {
int oemManaged = OEM_NONE;
if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_OEM_PAID)) {
@@ -256,30 +301,265 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> {
return oemManaged;
}
- @Override
- public int compareTo(NetworkIdentity another) {
- int res = Integer.compare(mType, another.mType);
+ /** @hide */
+ public static int compare(@NonNull NetworkIdentity left, @NonNull NetworkIdentity right) {
+ Objects.requireNonNull(right);
+ int res = Integer.compare(left.mType, right.mType);
if (res == 0) {
- res = Integer.compare(mSubType, another.mSubType);
+ res = Integer.compare(left.mRatType, right.mRatType);
}
- if (res == 0 && mSubscriberId != null && another.mSubscriberId != null) {
- res = mSubscriberId.compareTo(another.mSubscriberId);
+ if (res == 0 && left.mSubscriberId != null && right.mSubscriberId != null) {
+ res = left.mSubscriberId.compareTo(right.mSubscriberId);
}
- if (res == 0 && mNetworkId != null && another.mNetworkId != null) {
- res = mNetworkId.compareTo(another.mNetworkId);
+ if (res == 0 && left.mWifiNetworkKey != null && right.mWifiNetworkKey != null) {
+ res = left.mWifiNetworkKey.compareTo(right.mWifiNetworkKey);
}
if (res == 0) {
- res = Boolean.compare(mRoaming, another.mRoaming);
+ res = Boolean.compare(left.mRoaming, right.mRoaming);
}
if (res == 0) {
- res = Boolean.compare(mMetered, another.mMetered);
+ res = Boolean.compare(left.mMetered, right.mMetered);
}
if (res == 0) {
- res = Boolean.compare(mDefaultNetwork, another.mDefaultNetwork);
+ res = Boolean.compare(left.mDefaultNetwork, right.mDefaultNetwork);
}
if (res == 0) {
- res = Integer.compare(mOemManaged, another.mOemManaged);
+ res = Integer.compare(left.mOemManaged, right.mOemManaged);
}
return res;
}
+
+ /**
+ * Builder class for {@link NetworkIdentity}.
+ */
+ public static final class Builder {
+ // Need to be synchronized with ConnectivityManager.
+ // TODO: Use {@link ConnectivityManager#MAX_NETWORK_TYPE} when this file is in the module.
+ private static final int MAX_NETWORK_TYPE = 18; // TYPE_TEST
+ private static final int MIN_NETWORK_TYPE = TYPE_MOBILE;
+
+ private int mType;
+ private int mRatType;
+ private String mSubscriberId;
+ private String mWifiNetworkKey;
+ private boolean mRoaming;
+ private boolean mMetered;
+ private boolean mDefaultNetwork;
+ private int mOemManaged;
+
+ /**
+ * Creates a new Builder.
+ */
+ public Builder() {
+ // Initialize with default values. Will be overwritten by setters.
+ mType = ConnectivityManager.TYPE_NONE;
+ mRatType = NetworkTemplate.NETWORK_TYPE_ALL;
+ mSubscriberId = null;
+ mWifiNetworkKey = null;
+ mRoaming = false;
+ mMetered = false;
+ mDefaultNetwork = false;
+ mOemManaged = NetworkTemplate.OEM_MANAGED_NO;
+ }
+
+ /**
+ * Add an {@link NetworkStateSnapshot} into the {@link NetworkIdentity} instance.
+ * This is a useful shorthand that will read from the snapshot and set the
+ * following fields, if they are set in the snapshot :
+ * - type
+ * - subscriberId
+ * - roaming
+ * - metered
+ * - oemManaged
+ * - wifiNetworkKey
+ *
+ * @param snapshot The target {@link NetworkStateSnapshot} object.
+ * @return The builder object.
+ */
+ @SuppressLint("MissingGetterMatchingBuilder")
+ @NonNull
+ public Builder setNetworkStateSnapshot(@NonNull NetworkStateSnapshot snapshot) {
+ setType(snapshot.getLegacyType());
+
+ setSubscriberId(snapshot.getSubscriberId());
+ setRoaming(!snapshot.getNetworkCapabilities().hasCapability(
+ NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING));
+ setMetered(!(snapshot.getNetworkCapabilities().hasCapability(
+ NetworkCapabilities.NET_CAPABILITY_NOT_METERED)
+ || snapshot.getNetworkCapabilities().hasCapability(
+ NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED)));
+
+ setOemManaged(getOemBitfield(snapshot.getNetworkCapabilities()));
+
+ if (mType == TYPE_WIFI) {
+ final TransportInfo transportInfo = snapshot.getNetworkCapabilities()
+ .getTransportInfo();
+ if (transportInfo instanceof WifiInfo) {
+ final WifiInfo info = (WifiInfo) transportInfo;
+ setWifiNetworkKey(info.getNetworkKey());
+ }
+ }
+ return this;
+ }
+
+ /**
+ * Set the network type of the network.
+ *
+ * @param type the network type. See {@link ConnectivityManager#TYPE_*}.
+ *
+ * @return this builder.
+ */
+ @NonNull
+ public Builder setType(int type) {
+ // Include TYPE_NONE for compatibility, type field might not be filled by some
+ // networks such as test networks.
+ if ((type < MIN_NETWORK_TYPE || MAX_NETWORK_TYPE < type)
+ && type != ConnectivityManager.TYPE_NONE) {
+ throw new IllegalArgumentException("Invalid network type: " + type);
+ }
+ mType = type;
+ return this;
+ }
+
+ /**
+ * Set the Radio Access Technology(RAT) type of the network.
+ *
+ * No RAT type is specified by default. Call clearRatType to reset.
+ *
+ * @param ratType the Radio Access Technology(RAT) type if applicable. See
+ * {@code TelephonyManager.NETWORK_TYPE_*}.
+ *
+ * @return this builder.
+ */
+ @NonNull
+ public Builder setRatType(@Annotation.NetworkType int ratType) {
+ if (!CollectionUtils.contains(TelephonyManager.getAllNetworkTypes(), ratType)
+ && ratType != TelephonyManager.NETWORK_TYPE_UNKNOWN) {
+ throw new IllegalArgumentException("Invalid ratType " + ratType);
+ }
+ mRatType = ratType;
+ return this;
+ }
+
+ /**
+ * Clear the Radio Access Technology(RAT) type of the network.
+ *
+ * @return this builder.
+ */
+ @NonNull
+ public Builder clearRatType() {
+ mRatType = NetworkTemplate.NETWORK_TYPE_ALL;
+ return this;
+ }
+
+ /**
+ * Set the Subscriber Id.
+ *
+ * @param subscriberId the Subscriber Id of the network. Or null if not applicable.
+ * @return this builder.
+ */
+ @NonNull
+ public Builder setSubscriberId(@Nullable String subscriberId) {
+ mSubscriberId = subscriberId;
+ return this;
+ }
+
+ /**
+ * Set the Wifi Network Key.
+ *
+ * @param wifiNetworkKey Wifi Network Key of the network,
+ * see {@link WifiInfo#getNetworkKey()}.
+ * Or null if not applicable.
+ * @return this builder.
+ */
+ @NonNull
+ public Builder setWifiNetworkKey(@Nullable String wifiNetworkKey) {
+ mWifiNetworkKey = wifiNetworkKey;
+ return this;
+ }
+
+ /**
+ * Set whether this network is roaming.
+ *
+ * This field is false by default. Call with false to reset.
+ *
+ * @param roaming the roaming status of the network.
+ * @return this builder.
+ */
+ @NonNull
+ public Builder setRoaming(boolean roaming) {
+ mRoaming = roaming;
+ return this;
+ }
+
+ /**
+ * Set whether this network is metered.
+ *
+ * This field is false by default. Call with false to reset.
+ *
+ * @param metered the meteredness of the network.
+ * @return this builder.
+ */
+ @NonNull
+ public Builder setMetered(boolean metered) {
+ mMetered = metered;
+ return this;
+ }
+
+ /**
+ * Set whether this network is the default network.
+ *
+ * This field is false by default. Call with false to reset.
+ *
+ * @param defaultNetwork the default network status of the network.
+ * @return this builder.
+ */
+ @NonNull
+ public Builder setDefaultNetwork(boolean defaultNetwork) {
+ mDefaultNetwork = defaultNetwork;
+ return this;
+ }
+
+ /**
+ * Set the OEM managed type.
+ *
+ * @param oemManaged Type of OEM managed network or unmanaged networks.
+ * See {@code NetworkTemplate#OEM_MANAGED_*}.
+ * @return this builder.
+ */
+ @NonNull
+ public Builder setOemManaged(@OemManaged int oemManaged) {
+ // Assert input does not contain illegal oemManage bits.
+ if ((~SUPPORTED_OEM_MANAGED_TYPES & oemManaged) != 0) {
+ throw new IllegalArgumentException("Invalid value for OemManaged : " + oemManaged);
+ }
+ mOemManaged = oemManaged;
+ return this;
+ }
+
+ private void ensureValidParameters() {
+ // Assert non-mobile network cannot have a ratType.
+ if (mType != TYPE_MOBILE && mRatType != NetworkTemplate.NETWORK_TYPE_ALL) {
+ throw new IllegalArgumentException(
+ "Invalid ratType " + mRatType + " for type " + mType);
+ }
+
+ // Assert non-wifi network cannot have a wifi network key.
+ if (mType != TYPE_WIFI && mWifiNetworkKey != null) {
+ throw new IllegalArgumentException("Invalid wifi network key for type " + mType);
+ }
+ }
+
+ /**
+ * Builds the instance of the {@link NetworkIdentity}.
+ *
+ * @return the built instance of {@link NetworkIdentity}.
+ */
+ @NonNull
+ public NetworkIdentity build() {
+ ensureValidParameters();
+ return new NetworkIdentity(mType, mRatType, mSubscriberId, mWifiNetworkKey,
+ mRoaming, mMetered, mDefaultNetwork, mOemManaged);
+ }
+ }
}
diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentitySet.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentitySet.java
index abbebef85c8f..dfa347f6f12b 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentitySet.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentitySet.java
@@ -18,6 +18,7 @@ package android.net;
import static android.net.ConnectivityManager.TYPE_MOBILE;
+import android.annotation.NonNull;
import android.service.NetworkIdentitySetProto;
import android.util.proto.ProtoOutputStream;
@@ -25,6 +26,8 @@ import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
/**
* Identity of a {@code iface}, defined by the set of {@link NetworkIdentity}
@@ -32,8 +35,7 @@ import java.util.HashSet;
*
* @hide
*/
-public class NetworkIdentitySet extends HashSet<NetworkIdentity> implements
- Comparable<NetworkIdentitySet> {
+public class NetworkIdentitySet extends HashSet<NetworkIdentity> {
private static final int VERSION_INIT = 1;
private static final int VERSION_ADD_ROAMING = 2;
private static final int VERSION_ADD_NETWORK_ID = 3;
@@ -41,9 +43,19 @@ public class NetworkIdentitySet extends HashSet<NetworkIdentity> implements
private static final int VERSION_ADD_DEFAULT_NETWORK = 5;
private static final int VERSION_ADD_OEM_MANAGED_NETWORK = 6;
+ /**
+ * Construct a {@link NetworkIdentitySet} object.
+ */
public NetworkIdentitySet() {
+ super();
}
+ /** @hide */
+ public NetworkIdentitySet(@NonNull Set<NetworkIdentity> ident) {
+ super(ident);
+ }
+
+ /** @hide */
public NetworkIdentitySet(DataInput in) throws IOException {
final int version = in.readInt();
final int size = in.readInt();
@@ -52,7 +64,7 @@ public class NetworkIdentitySet extends HashSet<NetworkIdentity> implements
final int ignored = in.readInt();
}
final int type = in.readInt();
- final int subType = in.readInt();
+ final int ratType = in.readInt();
final String subscriberId = readOptionalString(in);
final String networkId;
if (version >= VERSION_ADD_NETWORK_ID) {
@@ -91,63 +103,73 @@ public class NetworkIdentitySet extends HashSet<NetworkIdentity> implements
oemNetCapabilities = NetworkIdentity.OEM_NONE;
}
- add(new NetworkIdentity(type, subType, subscriberId, networkId, roaming, metered,
+ add(new NetworkIdentity(type, ratType, subscriberId, networkId, roaming, metered,
defaultNetwork, oemNetCapabilities));
}
}
/**
* Method to serialize this object into a {@code DataOutput}.
+ * @hide
*/
public void writeToStream(DataOutput out) throws IOException {
out.writeInt(VERSION_ADD_OEM_MANAGED_NETWORK);
out.writeInt(size());
for (NetworkIdentity ident : this) {
out.writeInt(ident.getType());
- out.writeInt(ident.getSubType());
+ out.writeInt(ident.getRatType());
writeOptionalString(out, ident.getSubscriberId());
- writeOptionalString(out, ident.getNetworkId());
- out.writeBoolean(ident.getRoaming());
- out.writeBoolean(ident.getMetered());
- out.writeBoolean(ident.getDefaultNetwork());
+ writeOptionalString(out, ident.getWifiNetworkKey());
+ out.writeBoolean(ident.isRoaming());
+ out.writeBoolean(ident.isMetered());
+ out.writeBoolean(ident.isDefaultNetwork());
out.writeInt(ident.getOemManaged());
}
}
- /** @return whether any {@link NetworkIdentity} in this set is considered metered. */
+ /**
+ * @return whether any {@link NetworkIdentity} in this set is considered metered.
+ * @hide
+ */
public boolean isAnyMemberMetered() {
if (isEmpty()) {
return false;
}
for (NetworkIdentity ident : this) {
- if (ident.getMetered()) {
+ if (ident.isMetered()) {
return true;
}
}
return false;
}
- /** @return whether any {@link NetworkIdentity} in this set is considered roaming. */
+ /**
+ * @return whether any {@link NetworkIdentity} in this set is considered roaming.
+ * @hide
+ */
public boolean isAnyMemberRoaming() {
if (isEmpty()) {
return false;
}
for (NetworkIdentity ident : this) {
- if (ident.getRoaming()) {
+ if (ident.isRoaming()) {
return true;
}
}
return false;
}
- /** @return whether any {@link NetworkIdentity} in this set is considered on the default
- network. */
+ /**
+ * @return whether any {@link NetworkIdentity} in this set is considered on the default
+ * network.
+ * @hide
+ */
public boolean areAllMembersOnDefaultNetwork() {
if (isEmpty()) {
return true;
}
for (NetworkIdentity ident : this) {
- if (!ident.getDefaultNetwork()) {
+ if (!ident.isDefaultNetwork()) {
return false;
}
}
@@ -171,18 +193,20 @@ public class NetworkIdentitySet extends HashSet<NetworkIdentity> implements
}
}
- @Override
- public int compareTo(NetworkIdentitySet another) {
- if (isEmpty()) return -1;
- if (another.isEmpty()) return 1;
+ public static int compare(@NonNull NetworkIdentitySet left, @NonNull NetworkIdentitySet right) {
+ Objects.requireNonNull(left);
+ Objects.requireNonNull(right);
+ if (left.isEmpty()) return -1;
+ if (right.isEmpty()) return 1;
- final NetworkIdentity ident = iterator().next();
- final NetworkIdentity anotherIdent = another.iterator().next();
- return ident.compareTo(anotherIdent);
+ final NetworkIdentity leftIdent = left.iterator().next();
+ final NetworkIdentity rightIdent = right.iterator().next();
+ return NetworkIdentity.compare(leftIdent, rightIdent);
}
/**
* Method to dump this object into proto debug file.
+ * @hide
*/
public void dumpDebug(ProtoOutputStream proto, long tag) {
final long start = proto.start(tag);
diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkStateSnapshot.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkStateSnapshot.java
index 39156343924d..d577aa8fba54 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkStateSnapshot.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkStateSnapshot.java
@@ -73,9 +73,9 @@ public final class NetworkStateSnapshot implements Parcelable {
/** @hide */
public NetworkStateSnapshot(@NonNull Parcel in) {
- mNetwork = in.readParcelable(null);
- mNetworkCapabilities = in.readParcelable(null);
- mLinkProperties = in.readParcelable(null);
+ mNetwork = in.readParcelable(null, android.net.Network.class);
+ mNetworkCapabilities = in.readParcelable(null, android.net.NetworkCapabilities.class);
+ mLinkProperties = in.readParcelable(null, android.net.LinkProperties.class);
mSubscriberId = in.readString();
mLegacyType = in.readInt();
}
diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkStats.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkStats.java
index 9d532e7929a6..9175809d9c7c 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkStats.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkStats.java
@@ -41,6 +41,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -57,7 +58,7 @@ import java.util.function.Predicate;
*/
// @NotThreadSafe
@SystemApi
-public final class NetworkStats implements Parcelable {
+public final class NetworkStats implements Parcelable, Iterable<NetworkStats.Entry> {
private static final String TAG = "NetworkStats";
/**
@@ -678,6 +679,35 @@ public final class NetworkStats implements Parcelable {
}
/**
+ * Iterate over Entry objects.
+ *
+ * Return an iterator of this object that will iterate through all contained Entry objects.
+ *
+ * This iterator does not support concurrent modification and makes no guarantee of fail-fast
+ * behavior. If any method that can mutate the contents of this object is called while
+ * iteration is in progress, either inside the loop or in another thread, then behavior is
+ * undefined.
+ * The remove() method is not implemented and will throw UnsupportedOperationException.
+ * @hide
+ */
+ @SystemApi
+ @NonNull public Iterator<Entry> iterator() {
+ return new Iterator<Entry>() {
+ int mIndex = 0;
+
+ @Override
+ public boolean hasNext() {
+ return mIndex < size;
+ }
+
+ @Override
+ public Entry next() {
+ return getValues(mIndex++, null);
+ }
+ };
+ }
+
+ /**
* Return specific stats entry.
* @hide
*/
diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsCollection.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsCollection.java
index 9f9d73f88851..58ca21fdfad0 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsCollection.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsCollection.java
@@ -32,6 +32,8 @@ import static android.text.format.DateUtils.WEEK_IN_MILLIS;
import static com.android.net.module.util.NetworkStatsUtils.multiplySafeByRational;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.Binder;
import android.service.NetworkStatsCollectionKeyProto;
import android.service.NetworkStatsCollectionProto;
@@ -70,6 +72,7 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Objects;
+import java.util.Set;
/**
* Collection of {@link NetworkStatsHistory}, stored based on combined key of
@@ -77,6 +80,7 @@ import java.util.Objects;
*
* @hide
*/
+// @SystemApi(client = MODULE_LIBRARIES)
public class NetworkStatsCollection implements FileRotator.Reader, FileRotator.Writer {
private static final String TAG = NetworkStatsCollection.class.getSimpleName();
/** File header magic number: "ANET" */
@@ -100,15 +104,23 @@ public class NetworkStatsCollection implements FileRotator.Reader, FileRotator.W
private long mTotalBytes;
private boolean mDirty;
+ /**
+ * Construct a {@link NetworkStatsCollection} object.
+ *
+ * @param bucketDuration duration of the buckets in this object, in milliseconds.
+ * @hide
+ */
public NetworkStatsCollection(long bucketDuration) {
mBucketDuration = bucketDuration;
reset();
}
+ /** @hide */
public void clear() {
reset();
}
+ /** @hide */
public void reset() {
mStats.clear();
mStartMillis = Long.MAX_VALUE;
@@ -117,6 +129,7 @@ public class NetworkStatsCollection implements FileRotator.Reader, FileRotator.W
mDirty = false;
}
+ /** @hide */
public long getStartMillis() {
return mStartMillis;
}
@@ -124,6 +137,7 @@ public class NetworkStatsCollection implements FileRotator.Reader, FileRotator.W
/**
* Return first atomic bucket in this collection, which is more conservative
* than {@link #mStartMillis}.
+ * @hide
*/
public long getFirstAtomicBucketMillis() {
if (mStartMillis == Long.MAX_VALUE) {
@@ -133,26 +147,32 @@ public class NetworkStatsCollection implements FileRotator.Reader, FileRotator.W
}
}
+ /** @hide */
public long getEndMillis() {
return mEndMillis;
}
+ /** @hide */
public long getTotalBytes() {
return mTotalBytes;
}
+ /** @hide */
public boolean isDirty() {
return mDirty;
}
+ /** @hide */
public void clearDirty() {
mDirty = false;
}
+ /** @hide */
public boolean isEmpty() {
return mStartMillis == Long.MAX_VALUE && mEndMillis == Long.MIN_VALUE;
}
+ /** @hide */
@VisibleForTesting
public long roundUp(long time) {
if (time == Long.MIN_VALUE || time == Long.MAX_VALUE
@@ -168,6 +188,7 @@ public class NetworkStatsCollection implements FileRotator.Reader, FileRotator.W
}
}
+ /** @hide */
@VisibleForTesting
public long roundDown(long time) {
if (time == Long.MIN_VALUE || time == Long.MAX_VALUE
@@ -182,10 +203,12 @@ public class NetworkStatsCollection implements FileRotator.Reader, FileRotator.W
}
}
+ /** @hide */
public int[] getRelevantUids(@NetworkStatsAccess.Level int accessLevel) {
return getRelevantUids(accessLevel, Binder.getCallingUid());
}
+ /** @hide */
public int[] getRelevantUids(@NetworkStatsAccess.Level int accessLevel,
final int callerUid) {
final ArrayList<Integer> uids = new ArrayList<>();
@@ -206,6 +229,7 @@ public class NetworkStatsCollection implements FileRotator.Reader, FileRotator.W
/**
* Combine all {@link NetworkStatsHistory} in this collection which match
* the requested parameters.
+ * @hide
*/
public NetworkStatsHistory getHistory(NetworkTemplate template, SubscriptionPlan augmentPlan,
int uid, int set, int tag, int fields, long start, long end,
@@ -331,6 +355,7 @@ public class NetworkStatsCollection implements FileRotator.Reader, FileRotator.W
* @param end - end of the range, timestamp in milliseconds since the epoch.
* @param accessLevel - caller access level.
* @param callerUid - caller UID.
+ * @hide
*/
public NetworkStats getSummary(NetworkTemplate template, long start, long end,
@NetworkStatsAccess.Level int accessLevel, int callerUid) {
@@ -377,6 +402,7 @@ public class NetworkStatsCollection implements FileRotator.Reader, FileRotator.W
/**
* Record given {@link android.net.NetworkStats.Entry} into this collection.
+ * @hide
*/
public void recordData(NetworkIdentitySet ident, int uid, int set, int tag, long start,
long end, NetworkStats.Entry entry) {
@@ -387,8 +413,12 @@ public class NetworkStatsCollection implements FileRotator.Reader, FileRotator.W
/**
* Record given {@link NetworkStatsHistory} into this collection.
+ *
+ * @hide
*/
- private void recordHistory(Key key, NetworkStatsHistory history) {
+ public void recordHistory(@NonNull Key key, @NonNull NetworkStatsHistory history) {
+ Objects.requireNonNull(key);
+ Objects.requireNonNull(history);
if (history.size() == 0) return;
noteRecordedHistory(history.getStart(), history.getEnd(), history.getTotalBytes());
@@ -403,8 +433,11 @@ public class NetworkStatsCollection implements FileRotator.Reader, FileRotator.W
/**
* Record all {@link NetworkStatsHistory} contained in the given collection
* into this collection.
+ *
+ * @hide
*/
- public void recordCollection(NetworkStatsCollection another) {
+ public void recordCollection(@NonNull NetworkStatsCollection another) {
+ Objects.requireNonNull(another);
for (int i = 0; i < another.mStats.size(); i++) {
final Key key = another.mStats.keyAt(i);
final NetworkStatsHistory value = another.mStats.valueAt(i);
@@ -433,6 +466,7 @@ public class NetworkStatsCollection implements FileRotator.Reader, FileRotator.W
}
}
+ /** @hide */
@Override
public void read(InputStream in) throws IOException {
read((DataInput) new DataInputStream(in));
@@ -472,6 +506,7 @@ public class NetworkStatsCollection implements FileRotator.Reader, FileRotator.W
}
}
+ /** @hide */
@Override
public void write(OutputStream out) throws IOException {
write((DataOutput) new DataOutputStream(out));
@@ -514,6 +549,7 @@ public class NetworkStatsCollection implements FileRotator.Reader, FileRotator.W
* See {@code NetworkStatsService#maybeUpgradeLegacyStatsLocked}.
*
* @deprecated
+ * @hide
*/
@Deprecated
public void readLegacyNetwork(File file) throws IOException {
@@ -559,6 +595,7 @@ public class NetworkStatsCollection implements FileRotator.Reader, FileRotator.W
* See {@code NetworkStatsService#maybeUpgradeLegacyStatsLocked}.
*
* @deprecated
+ * @hide
*/
@Deprecated
public void readLegacyUid(File file, boolean onlyTags) throws IOException {
@@ -629,6 +666,7 @@ public class NetworkStatsCollection implements FileRotator.Reader, FileRotator.W
* Remove any {@link NetworkStatsHistory} attributed to the requested UID,
* moving any {@link NetworkStats#TAG_NONE} series to
* {@link TrafficStats#UID_REMOVED}.
+ * @hide
*/
public void removeUids(int[] uids) {
final ArrayList<Key> knownKeys = new ArrayList<>();
@@ -665,10 +703,11 @@ public class NetworkStatsCollection implements FileRotator.Reader, FileRotator.W
private ArrayList<Key> getSortedKeys() {
final ArrayList<Key> keys = new ArrayList<>();
keys.addAll(mStats.keySet());
- Collections.sort(keys);
+ Collections.sort(keys, (left, right) -> Key.compare(left, right));
return keys;
}
+ /** @hide */
public void dump(IndentingPrintWriter pw) {
for (Key key : getSortedKeys()) {
pw.print("ident="); pw.print(key.ident.toString());
@@ -683,6 +722,7 @@ public class NetworkStatsCollection implements FileRotator.Reader, FileRotator.W
}
}
+ /** @hide */
public void dumpDebug(ProtoOutputStream proto, long tag) {
final long start = proto.start(tag);
@@ -706,6 +746,7 @@ public class NetworkStatsCollection implements FileRotator.Reader, FileRotator.W
proto.end(start);
}
+ /** @hide */
public void dumpCheckin(PrintWriter pw, long start, long end) {
dumpCheckin(pw, start, end, NetworkTemplate.buildTemplateMobileWildcard(), "cell");
dumpCheckin(pw, start, end, NetworkTemplate.buildTemplateWifiWildcard(), "wifi");
@@ -768,16 +809,37 @@ public class NetworkStatsCollection implements FileRotator.Reader, FileRotator.W
return false;
}
- private static class Key implements Comparable<Key> {
+ /**
+ * the identifier that associate with the {@link NetworkStatsHistory} object to identify
+ * a certain record in the {@link NetworkStatsCollection} object.
+ */
+ public static class Key {
+ /** @hide */
public final NetworkIdentitySet ident;
+ /** @hide */
public final int uid;
+ /** @hide */
public final int set;
+ /** @hide */
public final int tag;
private final int mHashCode;
- Key(NetworkIdentitySet ident, int uid, int set, int tag) {
- this.ident = ident;
+ /**
+ * Construct a {@link Key} object.
+ *
+ * @param ident a Set of {@link NetworkIdentity} that associated with the record.
+ * @param uid Uid of the record.
+ * @param set Set of the record, see {@code NetworkStats#SET_*}.
+ * @param tag Tag of the record, see {@link TrafficStats#setThreadStatsTag(int)}.
+ */
+ public Key(@NonNull Set<NetworkIdentity> ident, int uid, int set, int tag) {
+ this(new NetworkIdentitySet(Objects.requireNonNull(ident)), uid, set, tag);
+ }
+
+ /** @hide */
+ public Key(@NonNull NetworkIdentitySet ident, int uid, int set, int tag) {
+ this.ident = Objects.requireNonNull(ident);
this.uid = uid;
this.set = set;
this.tag = tag;
@@ -790,7 +852,7 @@ public class NetworkStatsCollection implements FileRotator.Reader, FileRotator.W
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (obj instanceof Key) {
final Key key = (Key) obj;
return uid == key.uid && set == key.set && tag == key.tag
@@ -799,20 +861,22 @@ public class NetworkStatsCollection implements FileRotator.Reader, FileRotator.W
return false;
}
- @Override
- public int compareTo(Key another) {
+ /** @hide */
+ public static int compare(@NonNull Key left, @NonNull Key right) {
+ Objects.requireNonNull(left);
+ Objects.requireNonNull(right);
int res = 0;
- if (ident != null && another.ident != null) {
- res = ident.compareTo(another.ident);
+ if (left.ident != null && right.ident != null) {
+ res = NetworkIdentitySet.compare(left.ident, right.ident);
}
if (res == 0) {
- res = Integer.compare(uid, another.uid);
+ res = Integer.compare(left.uid, right.uid);
}
if (res == 0) {
- res = Integer.compare(set, another.set);
+ res = Integer.compare(left.set, right.set);
}
if (res == 0) {
- res = Integer.compare(tag, another.tag);
+ res = Integer.compare(left.tag, right.tag);
}
return res;
}
diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsHistory.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsHistory.java
index 428bc6df266a..78c137073aaa 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsHistory.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsHistory.java
@@ -16,6 +16,7 @@
package android.net;
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
import static android.net.NetworkStats.IFACE_ALL;
import static android.net.NetworkStats.SET_DEFAULT;
import static android.net.NetworkStats.TAG_NONE;
@@ -30,6 +31,8 @@ import static android.text.format.DateUtils.SECOND_IN_MILLIS;
import static com.android.net.module.util.NetworkStatsUtils.multiplySafeByRational;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.Parcel;
@@ -50,7 +53,9 @@ import java.io.DataOutput;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.ProtocolException;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
import java.util.Random;
/**
@@ -64,18 +69,25 @@ import java.util.Random;
*
* @hide
*/
-public class NetworkStatsHistory implements Parcelable {
+@SystemApi(client = MODULE_LIBRARIES)
+public final class NetworkStatsHistory implements Parcelable {
private static final int VERSION_INIT = 1;
private static final int VERSION_ADD_PACKETS = 2;
private static final int VERSION_ADD_ACTIVE = 3;
+ /** @hide */
public static final int FIELD_ACTIVE_TIME = 0x01;
+ /** @hide */
public static final int FIELD_RX_BYTES = 0x02;
+ /** @hide */
public static final int FIELD_RX_PACKETS = 0x04;
+ /** @hide */
public static final int FIELD_TX_BYTES = 0x08;
+ /** @hide */
public static final int FIELD_TX_PACKETS = 0x10;
+ /** @hide */
public static final int FIELD_OPERATIONS = 0x20;
-
+ /** @hide */
public static final int FIELD_ALL = 0xFFFFFFFF;
private long bucketDuration;
@@ -89,34 +101,171 @@ public class NetworkStatsHistory implements Parcelable {
private long[] operations;
private long totalBytes;
- public static class Entry {
+ /** @hide */
+ public NetworkStatsHistory(long bucketDuration, long[] bucketStart, long[] activeTime,
+ long[] rxBytes, long[] rxPackets, long[] txBytes, long[] txPackets,
+ long[] operations, int bucketCount, long totalBytes) {
+ this.bucketDuration = bucketDuration;
+ this.bucketStart = bucketStart;
+ this.activeTime = activeTime;
+ this.rxBytes = rxBytes;
+ this.rxPackets = rxPackets;
+ this.txBytes = txBytes;
+ this.txPackets = txPackets;
+ this.operations = operations;
+ this.bucketCount = bucketCount;
+ this.totalBytes = totalBytes;
+ }
+
+ /**
+ * An instance to represent a single record in a {@link NetworkStatsHistory} object.
+ */
+ public static final class Entry {
+ /** @hide */
public static final long UNKNOWN = -1;
+ /** @hide */
+ // TODO: Migrate all callers to get duration from the history object and remove this field.
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public long bucketDuration;
+ /** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public long bucketStart;
+ /** @hide */
public long activeTime;
+ /** @hide */
@UnsupportedAppUsage
public long rxBytes;
+ /** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public long rxPackets;
+ /** @hide */
@UnsupportedAppUsage
public long txBytes;
+ /** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public long txPackets;
+ /** @hide */
public long operations;
+ /** @hide */
+ Entry() {}
+
+ /**
+ * Construct a {@link Entry} instance to represent a single record in a
+ * {@link NetworkStatsHistory} object.
+ *
+ * @param bucketStart Start of period for this {@link Entry}, in milliseconds since the
+ * Unix epoch, see {@link java.lang.System#currentTimeMillis}.
+ * @param activeTime Active time for this {@link Entry}, in milliseconds.
+ * @param rxBytes Number of bytes received for this {@link Entry}. Statistics should
+ * represent the contents of IP packets, including IP headers.
+ * @param rxPackets Number of packets received for this {@link Entry}. Statistics should
+ * represent the contents of IP packets, including IP headers.
+ * @param txBytes Number of bytes transmitted for this {@link Entry}. Statistics should
+ * represent the contents of IP packets, including IP headers.
+ * @param txPackets Number of bytes transmitted for this {@link Entry}. Statistics should
+ * represent the contents of IP packets, including IP headers.
+ * @param operations count of network operations performed for this {@link Entry}. This can
+ * be used to derive bytes-per-operation.
+ */
+ public Entry(long bucketStart, long activeTime, long rxBytes,
+ long rxPackets, long txBytes, long txPackets, long operations) {
+ this.bucketStart = bucketStart;
+ this.activeTime = activeTime;
+ this.rxBytes = rxBytes;
+ this.rxPackets = rxPackets;
+ this.txBytes = txBytes;
+ this.txPackets = txPackets;
+ this.operations = operations;
+ }
+
+ /**
+ * Get start timestamp of the bucket's time interval, in milliseconds since the Unix epoch.
+ */
+ public long getBucketStart() {
+ return bucketStart;
+ }
+
+ /**
+ * Get active time of the bucket's time interval, in milliseconds.
+ */
+ public long getActiveTime() {
+ return activeTime;
+ }
+
+ /** Get number of bytes received for this {@link Entry}. */
+ public long getRxBytes() {
+ return rxBytes;
+ }
+
+ /** Get number of packets received for this {@link Entry}. */
+ public long getRxPackets() {
+ return rxPackets;
+ }
+
+ /** Get number of bytes transmitted for this {@link Entry}. */
+ public long getTxBytes() {
+ return txBytes;
+ }
+
+ /** Get number of packets transmitted for this {@link Entry}. */
+ public long getTxPackets() {
+ return txPackets;
+ }
+
+ /** Get count of network operations performed for this {@link Entry}. */
+ public long getOperations() {
+ return operations;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o.getClass() != getClass()) return false;
+ Entry entry = (Entry) o;
+ return bucketStart == entry.bucketStart
+ && activeTime == entry.activeTime && rxBytes == entry.rxBytes
+ && rxPackets == entry.rxPackets && txBytes == entry.txBytes
+ && txPackets == entry.txPackets && operations == entry.operations;
+ }
+
+ @Override
+ public int hashCode() {
+ return (int) (bucketStart * 2
+ + activeTime * 3
+ + rxBytes * 5
+ + rxPackets * 7
+ + txBytes * 11
+ + txPackets * 13
+ + operations * 17);
+ }
+
+ @Override
+ public String toString() {
+ return "Entry{"
+ + "bucketStart=" + bucketStart
+ + ", activeTime=" + activeTime
+ + ", rxBytes=" + rxBytes
+ + ", rxPackets=" + rxPackets
+ + ", txBytes=" + txBytes
+ + ", txPackets=" + txPackets
+ + ", operations=" + operations
+ + "}";
+ }
}
+ /** @hide */
@UnsupportedAppUsage
public NetworkStatsHistory(long bucketDuration) {
this(bucketDuration, 10, FIELD_ALL);
}
+ /** @hide */
public NetworkStatsHistory(long bucketDuration, int initialSize) {
this(bucketDuration, initialSize, FIELD_ALL);
}
+ /** @hide */
public NetworkStatsHistory(long bucketDuration, int initialSize, int fields) {
this.bucketDuration = bucketDuration;
bucketStart = new long[initialSize];
@@ -130,11 +279,13 @@ public class NetworkStatsHistory implements Parcelable {
totalBytes = 0;
}
+ /** @hide */
public NetworkStatsHistory(NetworkStatsHistory existing, long bucketDuration) {
this(bucketDuration, existing.estimateResizeBuckets(bucketDuration));
recordEntireHistory(existing);
}
+ /** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public NetworkStatsHistory(Parcel in) {
bucketDuration = in.readLong();
@@ -150,7 +301,7 @@ public class NetworkStatsHistory implements Parcelable {
}
@Override
- public void writeToParcel(Parcel out, int flags) {
+ public void writeToParcel(@NonNull Parcel out, int flags) {
out.writeLong(bucketDuration);
writeLongArray(out, bucketStart, bucketCount);
writeLongArray(out, activeTime, bucketCount);
@@ -162,6 +313,7 @@ public class NetworkStatsHistory implements Parcelable {
out.writeLong(totalBytes);
}
+ /** @hide */
public NetworkStatsHistory(DataInput in) throws IOException {
final int version = in.readInt();
switch (version) {
@@ -204,6 +356,7 @@ public class NetworkStatsHistory implements Parcelable {
}
}
+ /** @hide */
public void writeToStream(DataOutput out) throws IOException {
out.writeInt(VERSION_ADD_ACTIVE);
out.writeLong(bucketDuration);
@@ -221,15 +374,18 @@ public class NetworkStatsHistory implements Parcelable {
return 0;
}
+ /** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public int size() {
return bucketCount;
}
+ /** @hide */
public long getBucketDuration() {
return bucketDuration;
}
+ /** @hide */
@UnsupportedAppUsage
public long getStart() {
if (bucketCount > 0) {
@@ -239,6 +395,7 @@ public class NetworkStatsHistory implements Parcelable {
}
}
+ /** @hide */
@UnsupportedAppUsage
public long getEnd() {
if (bucketCount > 0) {
@@ -250,6 +407,7 @@ public class NetworkStatsHistory implements Parcelable {
/**
* Return total bytes represented by this history.
+ * @hide
*/
public long getTotalBytes() {
return totalBytes;
@@ -258,6 +416,7 @@ public class NetworkStatsHistory implements Parcelable {
/**
* Return index of bucket that contains or is immediately before the
* requested time.
+ * @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public int getIndexBefore(long time) {
@@ -273,6 +432,7 @@ public class NetworkStatsHistory implements Parcelable {
/**
* Return index of bucket that contains or is immediately after the
* requested time.
+ * @hide
*/
public int getIndexAfter(long time) {
int index = Arrays.binarySearch(bucketStart, 0, bucketCount, time);
@@ -286,6 +446,7 @@ public class NetworkStatsHistory implements Parcelable {
/**
* Return specific stats entry.
+ * @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public Entry getValues(int i, Entry recycle) {
@@ -301,6 +462,23 @@ public class NetworkStatsHistory implements Parcelable {
return entry;
}
+ /**
+ * Get List of {@link Entry} of the {@link NetworkStatsHistory} instance.
+ *
+ * @return
+ */
+ @NonNull
+ public List<Entry> getEntries() {
+ // TODO: Return a wrapper that uses this list instead, to prevent the returned result
+ // from being changed.
+ final ArrayList<Entry> ret = new ArrayList<>(size());
+ for (int i = 0; i < size(); i++) {
+ ret.add(getValues(i, null /* recycle */));
+ }
+ return ret;
+ }
+
+ /** @hide */
public void setValues(int i, Entry entry) {
// Unwind old values
if (rxBytes != null) totalBytes -= rxBytes[i];
@@ -322,6 +500,7 @@ public class NetworkStatsHistory implements Parcelable {
/**
* Record that data traffic occurred in the given time range. Will
* distribute across internal buckets, creating new buckets as needed.
+ * @hide
*/
@Deprecated
public void recordData(long start, long end, long rxBytes, long txBytes) {
@@ -332,6 +511,7 @@ public class NetworkStatsHistory implements Parcelable {
/**
* Record that data traffic occurred in the given time range. Will
* distribute across internal buckets, creating new buckets as needed.
+ * @hide
*/
public void recordData(long start, long end, NetworkStats.Entry entry) {
long rxBytes = entry.rxBytes;
@@ -392,6 +572,7 @@ public class NetworkStatsHistory implements Parcelable {
/**
* Record an entire {@link NetworkStatsHistory} into this history. Usually
* for combining together stats for external reporting.
+ * @hide
*/
@UnsupportedAppUsage
public void recordEntireHistory(NetworkStatsHistory input) {
@@ -402,6 +583,7 @@ public class NetworkStatsHistory implements Parcelable {
* Record given {@link NetworkStatsHistory} into this history, copying only
* buckets that atomically occur in the inclusive time range. Doesn't
* interpolate across partial buckets.
+ * @hide
*/
public void recordHistory(NetworkStatsHistory input, long start, long end) {
final NetworkStats.Entry entry = new NetworkStats.Entry(
@@ -483,6 +665,7 @@ public class NetworkStatsHistory implements Parcelable {
/**
* Clear all data stored in this object.
+ * @hide
*/
public void clear() {
bucketStart = EmptyArray.LONG;
@@ -498,9 +681,10 @@ public class NetworkStatsHistory implements Parcelable {
/**
* Remove buckets older than requested cutoff.
+ * @hide
*/
- @Deprecated
public void removeBucketsBefore(long cutoff) {
+ // TODO: Consider use getIndexBefore.
int i;
for (i = 0; i < bucketCount; i++) {
final long curStart = bucketStart[i];
@@ -522,7 +706,9 @@ public class NetworkStatsHistory implements Parcelable {
if (operations != null) operations = Arrays.copyOfRange(operations, i, length);
bucketCount -= i;
- // TODO: subtract removed values from totalBytes
+ totalBytes = 0;
+ if (rxBytes != null) totalBytes += CollectionUtils.total(rxBytes);
+ if (txBytes != null) totalBytes += CollectionUtils.total(txBytes);
}
}
@@ -536,6 +722,7 @@ public class NetworkStatsHistory implements Parcelable {
* @param start - start of the range, timestamp in milliseconds since the epoch.
* @param end - end of the range, timestamp in milliseconds since the epoch.
* @param recycle - entry instance for performance, could be null.
+ * @hide
*/
@UnsupportedAppUsage
public Entry getValues(long start, long end, Entry recycle) {
@@ -550,6 +737,7 @@ public class NetworkStatsHistory implements Parcelable {
* @param end - end of the range, timestamp in milliseconds since the epoch.
* @param now - current timestamp in milliseconds since the epoch (wall clock).
* @param recycle - entry instance for performance, could be null.
+ * @hide
*/
@UnsupportedAppUsage
public Entry getValues(long start, long end, long now, Entry recycle) {
@@ -613,6 +801,7 @@ public class NetworkStatsHistory implements Parcelable {
/**
* @deprecated only for temporary testing
+ * @hide
*/
@Deprecated
public void generateRandom(long start, long end, long bytes) {
@@ -631,6 +820,7 @@ public class NetworkStatsHistory implements Parcelable {
/**
* @deprecated only for temporary testing
+ * @hide
*/
@Deprecated
public void generateRandom(long start, long end, long rxBytes, long rxPackets, long txBytes,
@@ -660,12 +850,14 @@ public class NetworkStatsHistory implements Parcelable {
}
}
+ /** @hide */
public static long randomLong(Random r, long start, long end) {
return (long) (start + (r.nextFloat() * (end - start)));
}
/**
* Quickly determine if this history intersects with given window.
+ * @hide
*/
public boolean intersects(long start, long end) {
final long dataStart = getStart();
@@ -677,6 +869,7 @@ public class NetworkStatsHistory implements Parcelable {
return false;
}
+ /** @hide */
public void dump(IndentingPrintWriter pw, boolean fullHistory) {
pw.print("NetworkStatsHistory: bucketDuration=");
pw.println(bucketDuration / SECOND_IN_MILLIS);
@@ -700,6 +893,7 @@ public class NetworkStatsHistory implements Parcelable {
pw.decreaseIndent();
}
+ /** @hide */
public void dumpCheckin(PrintWriter pw) {
pw.print("d,");
pw.print(bucketDuration / SECOND_IN_MILLIS);
@@ -717,6 +911,7 @@ public class NetworkStatsHistory implements Parcelable {
}
}
+ /** @hide */
public void dumpDebug(ProtoOutputStream proto, long tag) {
final long start = proto.start(tag);
@@ -776,6 +971,7 @@ public class NetworkStatsHistory implements Parcelable {
if (array != null) array[i] += value;
}
+ /** @hide */
public int estimateResizeBuckets(long newBucketDuration) {
return (int) (size() * getBucketDuration() / newBucketDuration);
}
@@ -783,6 +979,7 @@ public class NetworkStatsHistory implements Parcelable {
/**
* Utility methods for interacting with {@link DataInputStream} and
* {@link DataOutputStream}, mostly dealing with writing partial arrays.
+ * @hide
*/
public static class DataStreamUtils {
@Deprecated
@@ -857,6 +1054,7 @@ public class NetworkStatsHistory implements Parcelable {
/**
* Utility methods for interacting with {@link Parcel} structures, mostly
* dealing with writing partial arrays.
+ * @hide
*/
public static class ParcelUtils {
public static long[] readLongArray(Parcel in) {
@@ -884,4 +1082,80 @@ public class NetworkStatsHistory implements Parcelable {
}
}
+ /**
+ * Builder class for {@link NetworkStatsHistory}.
+ */
+ public static final class Builder {
+ private final long mBucketDuration;
+ private final List<Long> mBucketStart;
+ private final List<Long> mActiveTime;
+ private final List<Long> mRxBytes;
+ private final List<Long> mRxPackets;
+ private final List<Long> mTxBytes;
+ private final List<Long> mTxPackets;
+ private final List<Long> mOperations;
+
+ /**
+ * Creates a new Builder with given bucket duration and initial capacity to construct
+ * {@link NetworkStatsHistory} objects.
+ *
+ * @param bucketDuration Duration of the buckets of the object, in milliseconds.
+ * @param initialCapacity Estimated number of records.
+ */
+ public Builder(long bucketDuration, int initialCapacity) {
+ mBucketDuration = bucketDuration;
+ mBucketStart = new ArrayList<>(initialCapacity);
+ mActiveTime = new ArrayList<>(initialCapacity);
+ mRxBytes = new ArrayList<>(initialCapacity);
+ mRxPackets = new ArrayList<>(initialCapacity);
+ mTxBytes = new ArrayList<>(initialCapacity);
+ mTxPackets = new ArrayList<>(initialCapacity);
+ mOperations = new ArrayList<>(initialCapacity);
+ }
+
+ /**
+ * Add an {@link Entry} into the {@link NetworkStatsHistory} instance.
+ *
+ * @param entry The target {@link Entry} object.
+ * @return The builder object.
+ */
+ @NonNull
+ public Builder addEntry(@NonNull Entry entry) {
+ mBucketStart.add(entry.bucketStart);
+ mActiveTime.add(entry.activeTime);
+ mRxBytes.add(entry.rxBytes);
+ mRxPackets.add(entry.rxPackets);
+ mTxBytes.add(entry.txBytes);
+ mTxPackets.add(entry.txPackets);
+ mOperations.add(entry.operations);
+ return this;
+ }
+
+ private static long sum(@NonNull List<Long> list) {
+ long sum = 0;
+ for (long entry : list) {
+ sum += entry;
+ }
+ return sum;
+ }
+
+ /**
+ * Builds the instance of the {@link NetworkStatsHistory}.
+ *
+ * @return the built instance of {@link NetworkStatsHistory}.
+ */
+ @NonNull
+ public NetworkStatsHistory build() {
+ return new NetworkStatsHistory(mBucketDuration,
+ CollectionUtils.toLongArray(mBucketStart),
+ CollectionUtils.toLongArray(mActiveTime),
+ CollectionUtils.toLongArray(mRxBytes),
+ CollectionUtils.toLongArray(mRxPackets),
+ CollectionUtils.toLongArray(mTxBytes),
+ CollectionUtils.toLongArray(mTxPackets),
+ CollectionUtils.toLongArray(mOperations),
+ mBucketStart.size(),
+ sum(mRxBytes) + sum(mTxBytes));
+ }
+ }
}
diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkTemplate.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkTemplate.java
index e9084b019668..cad80752b8e7 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkTemplate.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkTemplate.java
@@ -263,7 +263,7 @@ public final class NetworkTemplate implements Parcelable {
* Template to match {@link ConnectivityManager#TYPE_WIFI} networks with the
* given key of the wifi network.
*
- * @param wifiNetworkKey key of the wifi network. see {@link WifiInfo#getCurrentNetworkKey()}
+ * @param wifiNetworkKey key of the wifi network. see {@link WifiInfo#getNetworkKey()}
* to know details about the key.
* @hide
*/
@@ -283,7 +283,7 @@ public final class NetworkTemplate implements Parcelable {
* Call with {@link #WIFI_NETWORK_KEY_ALL} for {@code wifiNetworkKey} to get result regardless
* of key of the wifi network.
*
- * @param wifiNetworkKey key of the wifi network. see {@link WifiInfo#getCurrentNetworkKey()}
+ * @param wifiNetworkKey key of the wifi network. see {@link WifiInfo#getNetworkKey()}
* to know details about the key.
* @param subscriberId the IMSI associated to this wifi network.
*
@@ -364,7 +364,7 @@ public final class NetworkTemplate implements Parcelable {
private final int mMetered;
private final int mRoaming;
private final int mDefaultNetwork;
- private final int mSubType;
+ private final int mRatType;
/**
* The subscriber Id match rule defines how the template should match networks with
* specific subscriberId(s). See NetworkTemplate#SUBSCRIBER_ID_MATCH_RULE_* for more detail.
@@ -413,18 +413,18 @@ public final class NetworkTemplate implements Parcelable {
/** @hide */
// TODO: Remove it after updating all of the caller.
public NetworkTemplate(int matchRule, String subscriberId, String[] matchSubscriberIds,
- String wifiNetworkKey, int metered, int roaming, int defaultNetwork, int subType,
+ String wifiNetworkKey, int metered, int roaming, int defaultNetwork, int ratType,
int oemManaged) {
this(matchRule, subscriberId, matchSubscriberIds,
wifiNetworkKey != null ? new String[] { wifiNetworkKey } : new String[0],
- metered, roaming, defaultNetwork, subType, oemManaged,
+ metered, roaming, defaultNetwork, ratType, oemManaged,
NetworkStatsUtils.SUBSCRIBER_ID_MATCH_RULE_EXACT);
}
/** @hide */
public NetworkTemplate(int matchRule, String subscriberId, String[] matchSubscriberIds,
String[] matchWifiNetworkKeys, int metered, int roaming,
- int defaultNetwork, int subType, int oemManaged, int subscriberIdMatchRule) {
+ int defaultNetwork, int ratType, int oemManaged, int subscriberIdMatchRule) {
Objects.requireNonNull(matchWifiNetworkKeys);
mMatchRule = matchRule;
mSubscriberId = subscriberId;
@@ -435,7 +435,7 @@ public final class NetworkTemplate implements Parcelable {
mMetered = metered;
mRoaming = roaming;
mDefaultNetwork = defaultNetwork;
- mSubType = subType;
+ mRatType = ratType;
mOemManaged = oemManaged;
mSubscriberIdMatchRule = subscriberIdMatchRule;
checkValidSubscriberIdMatchRule(matchRule, subscriberIdMatchRule);
@@ -453,7 +453,7 @@ public final class NetworkTemplate implements Parcelable {
mMetered = in.readInt();
mRoaming = in.readInt();
mDefaultNetwork = in.readInt();
- mSubType = in.readInt();
+ mRatType = in.readInt();
mOemManaged = in.readInt();
mSubscriberIdMatchRule = in.readInt();
}
@@ -467,7 +467,7 @@ public final class NetworkTemplate implements Parcelable {
dest.writeInt(mMetered);
dest.writeInt(mRoaming);
dest.writeInt(mDefaultNetwork);
- dest.writeInt(mSubType);
+ dest.writeInt(mRatType);
dest.writeInt(mOemManaged);
dest.writeInt(mSubscriberIdMatchRule);
}
@@ -500,8 +500,8 @@ public final class NetworkTemplate implements Parcelable {
builder.append(", defaultNetwork=").append(NetworkStats.defaultNetworkToString(
mDefaultNetwork));
}
- if (mSubType != NETWORK_TYPE_ALL) {
- builder.append(", subType=").append(mSubType);
+ if (mRatType != NETWORK_TYPE_ALL) {
+ builder.append(", ratType=").append(mRatType);
}
if (mOemManaged != OEM_MANAGED_ALL) {
builder.append(", oemManaged=").append(getOemManagedNames(mOemManaged));
@@ -514,7 +514,7 @@ public final class NetworkTemplate implements Parcelable {
@Override
public int hashCode() {
return Objects.hash(mMatchRule, mSubscriberId, Arrays.hashCode(mMatchWifiNetworkKeys),
- mMetered, mRoaming, mDefaultNetwork, mSubType, mOemManaged, mSubscriberIdMatchRule);
+ mMetered, mRoaming, mDefaultNetwork, mRatType, mOemManaged, mSubscriberIdMatchRule);
}
@Override
@@ -526,7 +526,7 @@ public final class NetworkTemplate implements Parcelable {
&& mMetered == other.mMetered
&& mRoaming == other.mRoaming
&& mDefaultNetwork == other.mDefaultNetwork
- && mSubType == other.mSubType
+ && mRatType == other.mRatType
&& mOemManaged == other.mOemManaged
&& mSubscriberIdMatchRule == other.mSubscriberIdMatchRule
&& Arrays.equals(mMatchWifiNetworkKeys, other.mMatchWifiNetworkKeys);
@@ -593,7 +593,7 @@ public final class NetworkTemplate implements Parcelable {
/**
* Get the set of Wifi Network Keys of the template.
- * See {@link WifiInfo#getCurrentNetworkKey()}.
+ * See {@link WifiInfo#getNetworkKey()}.
*/
@NonNull
public Set<String> getWifiNetworkKeys() {
@@ -635,7 +635,7 @@ public final class NetworkTemplate implements Parcelable {
* Get the Radio Access Technology(RAT) type filter of the template.
*/
public int getRatType() {
- return mSubType;
+ return mRatType;
}
/**
@@ -708,8 +708,8 @@ public final class NetworkTemplate implements Parcelable {
}
private boolean matchesCollapsedRatType(NetworkIdentity ident) {
- return mSubType == NETWORK_TYPE_ALL
- || getCollapsedRatType(mSubType) == getCollapsedRatType(ident.mSubType);
+ return mRatType == NETWORK_TYPE_ALL
+ || getCollapsedRatType(mRatType) == getCollapsedRatType(ident.mRatType);
}
/**
@@ -729,7 +729,7 @@ public final class NetworkTemplate implements Parcelable {
* Returns true when the key matches, or when {@code mMatchWifiNetworkKeys} is
* empty.
*
- * @param wifiNetworkKey key of the wifi network. see {@link WifiInfo#getCurrentNetworkKey()}
+ * @param wifiNetworkKey key of the wifi network. see {@link WifiInfo#getNetworkKey()}
* to know details about the key.
*/
private boolean matchesWifiNetworkKey(@NonNull String wifiNetworkKey) {
@@ -837,7 +837,7 @@ public final class NetworkTemplate implements Parcelable {
switch (ident.mType) {
case TYPE_WIFI:
return matchesSubscriberId(ident.mSubscriberId)
- && matchesWifiNetworkKey(ident.mNetworkId);
+ && matchesWifiNetworkKey(ident.mWifiNetworkKey);
default:
return false;
}
@@ -1059,9 +1059,9 @@ public final class NetworkTemplate implements Parcelable {
* the intention of matching any Wifi Network Key.
*
* @param wifiNetworkKeys the list of Wifi Network Key,
- * see {@link WifiInfo#getCurrentNetworkKey()}.
+ * see {@link WifiInfo#getNetworkKey()}.
* Or an empty list to match all networks.
- * Note that {@code getCurrentNetworkKey()} might get null key
+ * Note that {@code getNetworkKey()} might get null key
* when wifi disconnects. However, the caller should never invoke
* this function with a null Wifi Network Key since such statistics
* never exists.
diff --git a/packages/ConnectivityT/framework-t/src/android/net/TrafficStats.java b/packages/ConnectivityT/framework-t/src/android/net/TrafficStats.java
index 1af32bf5524c..c803a723ba83 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/TrafficStats.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/TrafficStats.java
@@ -17,7 +17,6 @@
package android.net;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.TestApi;
@@ -27,8 +26,8 @@ import android.app.usage.NetworkStatsManager;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.media.MediaPlayer;
+import android.os.Binder;
import android.os.Build;
-import android.os.IBinder;
import android.os.RemoteException;
import com.android.server.NetworkManagementSocketTagger;
@@ -37,8 +36,6 @@ import dalvik.system.SocketTagger;
import java.io.FileDescriptor;
import java.io.IOException;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
import java.net.DatagramSocket;
import java.net.Socket;
import java.net.SocketException;
@@ -177,25 +174,12 @@ public class TrafficStats {
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 130143562)
private synchronized static INetworkStatsService getStatsService() {
if (sStatsService == null) {
- sStatsService = getStatsBinder();
+ throw new IllegalStateException("TrafficStats not initialized, uid="
+ + Binder.getCallingUid());
}
return sStatsService;
}
- @Nullable
- private static INetworkStatsService getStatsBinder() {
- try {
- final Method getServiceMethod = Class.forName("android.os.ServiceManager")
- .getDeclaredMethod("getService", new Class[]{String.class});
- final IBinder binder = (IBinder) getServiceMethod.invoke(
- null, Context.NETWORK_STATS_SERVICE);
- return INetworkStatsService.Stub.asInterface(binder);
- } catch (NoSuchMethodException | ClassNotFoundException | IllegalAccessException
- | InvocationTargetException e) {
- throw new NullPointerException("Cannot get INetworkStatsService: " + e);
- }
- }
-
/**
* Snapshot of {@link NetworkStats} when the currently active profiling
* session started, or {@code null} if no session active.
@@ -210,6 +194,26 @@ public class TrafficStats {
private static final String LOOPBACK_IFACE = "lo";
/**
+ * Initialization {@link TrafficStats} with the context, to
+ * allow {@link TrafficStats} to fetch the needed binder.
+ *
+ * @param context a long-lived context, such as the application context or system
+ * server context.
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @SuppressLint("VisiblySynchronized")
+ public static synchronized void init(@NonNull final Context context) {
+ if (sStatsService != null) {
+ throw new IllegalStateException("TrafficStats is already initialized, uid="
+ + Binder.getCallingUid());
+ }
+ final NetworkStatsManager statsManager =
+ context.getSystemService(NetworkStatsManager.class);
+ sStatsService = statsManager.getBinder();
+ }
+
+ /**
* Set active tag to use when accounting {@link Socket} traffic originating
* from the current thread. Only one active tag per thread is supported.
* <p>
diff --git a/packages/ConnectivityT/framework-t/src/android/net/UnderlyingNetworkInfo.java b/packages/ConnectivityT/framework-t/src/android/net/UnderlyingNetworkInfo.java
index 33f9375c03bf..7ab53b1da856 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/UnderlyingNetworkInfo.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/UnderlyingNetworkInfo.java
@@ -60,7 +60,7 @@ public final class UnderlyingNetworkInfo implements Parcelable {
mOwnerUid = in.readInt();
mIface = in.readString();
List<String> underlyingIfaces = new ArrayList<>();
- in.readList(underlyingIfaces, null /*classLoader*/);
+ in.readList(underlyingIfaces, null /*classLoader*/, java.lang.String.class);
mUnderlyingIfaces = Collections.unmodifiableList(underlyingIfaces);
}
diff --git a/packages/ConnectivityT/service/Android.bp b/packages/ConnectivityT/service/Android.bp
index b261e165a112..24bc91d91ef7 100644
--- a/packages/ConnectivityT/service/Android.bp
+++ b/packages/ConnectivityT/service/Android.bp
@@ -26,6 +26,8 @@ filegroup {
srcs: [
"src/com/android/server/net/NetworkIdentity*.java",
"src/com/android/server/net/NetworkStats*.java",
+ "src/com/android/server/net/BpfInterfaceMapUpdater.java",
+ "src/com/android/server/net/InterfaceMapValue.java",
],
path: "src",
visibility: [
@@ -66,6 +68,7 @@ filegroup {
filegroup {
name: "services.connectivity-ethernet-sources",
srcs: [
+ "src/com/android/server/net/DelayedDiskWrite.java",
"src/com/android/server/net/IpConfigStore.java",
],
path: "src",
@@ -97,3 +100,28 @@ filegroup {
"//packages/modules/Connectivity:__subpackages__",
],
}
+
+cc_library_shared {
+ name: "libcom_android_net_module_util_jni",
+ min_sdk_version: "30",
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wno-unused-parameter",
+ "-Wthread-safety",
+ ],
+ srcs: [
+ "jni/onload.cpp",
+ ],
+ stl: "libc++_static",
+ static_libs: [
+ "libnet_utils_device_common_bpfjni",
+ ],
+ shared_libs: [
+ "liblog",
+ "libnativehelper",
+ ],
+ apex_available: [
+ "//apex_available:platform",
+ ],
+}
diff --git a/packages/ConnectivityT/service/jni/onload.cpp b/packages/ConnectivityT/service/jni/onload.cpp
new file mode 100644
index 000000000000..bca469756095
--- /dev/null
+++ b/packages/ConnectivityT/service/jni/onload.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <nativehelper/JNIHelp.h>
+#include <log/log.h>
+
+namespace android {
+
+int register_com_android_net_module_util_BpfMap(JNIEnv* env, char const* class_name);
+
+extern "C" jint JNI_OnLoad(JavaVM* vm, void*) {
+ JNIEnv *env;
+ if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
+ ALOGE("GetEnv failed");
+ return JNI_ERR;
+ }
+
+ if (register_com_android_net_module_util_BpfMap(env,
+ "com/android/net/module/util/BpfMap") < 0) return JNI_ERR;
+
+ return JNI_VERSION_1_6;
+}
+
+};
+
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/BpfInterfaceMapUpdater.java b/packages/ConnectivityT/service/src/com/android/server/net/BpfInterfaceMapUpdater.java
new file mode 100644
index 000000000000..25c88eb6bdb2
--- /dev/null
+++ b/packages/ConnectivityT/service/src/com/android/server/net/BpfInterfaceMapUpdater.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.net;
+
+import android.content.Context;
+import android.net.INetd;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceSpecificException;
+import android.system.ErrnoException;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.net.module.util.BaseNetdUnsolicitedEventListener;
+import com.android.net.module.util.BpfMap;
+import com.android.net.module.util.IBpfMap;
+import com.android.net.module.util.InterfaceParams;
+import com.android.net.module.util.Struct.U32;
+
+/**
+ * Monitor interface added (without removed) and right interface name and its index to bpf map.
+ */
+public class BpfInterfaceMapUpdater {
+ private static final String TAG = BpfInterfaceMapUpdater.class.getSimpleName();
+ // This is current path but may be changed soon.
+ private static final String IFACE_INDEX_NAME_MAP_PATH =
+ "/sys/fs/bpf/map_netd_iface_index_name_map";
+ private final IBpfMap<U32, InterfaceMapValue> mBpfMap;
+ private final INetd mNetd;
+ private final Handler mHandler;
+ private final Dependencies mDeps;
+
+ public BpfInterfaceMapUpdater(Context ctx, Handler handler) {
+ this(ctx, handler, new Dependencies());
+ }
+
+ @VisibleForTesting
+ public BpfInterfaceMapUpdater(Context ctx, Handler handler, Dependencies deps) {
+ mDeps = deps;
+ mBpfMap = deps.getInterfaceMap();
+ mNetd = deps.getINetd(ctx);
+ mHandler = handler;
+ }
+
+ /**
+ * Dependencies of BpfInerfaceMapUpdater, for injection in tests.
+ */
+ @VisibleForTesting
+ public static class Dependencies {
+ /** Create BpfMap for updating interface and index mapping. */
+ public IBpfMap<U32, InterfaceMapValue> getInterfaceMap() {
+ try {
+ return new BpfMap<>(IFACE_INDEX_NAME_MAP_PATH, BpfMap.BPF_F_RDWR,
+ U32.class, InterfaceMapValue.class);
+ } catch (ErrnoException e) {
+ Log.e(TAG, "Cannot create interface map: " + e);
+ return null;
+ }
+ }
+
+ /** Get InterfaceParams for giving interface name. */
+ public InterfaceParams getInterfaceParams(String ifaceName) {
+ return InterfaceParams.getByName(ifaceName);
+ }
+
+ /** Get INetd binder object. */
+ public INetd getINetd(Context ctx) {
+ return INetd.Stub.asInterface((IBinder) ctx.getSystemService(Context.NETD_SERVICE));
+ }
+ }
+
+ /**
+ * Start listening interface update event.
+ * Query current interface names before listening.
+ */
+ public void start() {
+ mHandler.post(() -> {
+ if (mBpfMap == null) {
+ Log.wtf(TAG, "Fail to start: Null bpf map");
+ return;
+ }
+
+ try {
+ // TODO: use a NetlinkMonitor and listen for RTM_NEWLINK messages instead.
+ mNetd.registerUnsolicitedEventListener(new InterfaceChangeObserver());
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Unable to register netd UnsolicitedEventListener, " + e);
+ }
+
+ final String[] ifaces;
+ try {
+ // TODO: use a netlink dump to get the current interface list.
+ ifaces = mNetd.interfaceGetList();
+ } catch (RemoteException | ServiceSpecificException e) {
+ Log.wtf(TAG, "Unable to query interface names by netd, " + e);
+ return;
+ }
+
+ for (String ifaceName : ifaces) {
+ addInterface(ifaceName);
+ }
+ });
+ }
+
+ private void addInterface(String ifaceName) {
+ final InterfaceParams iface = mDeps.getInterfaceParams(ifaceName);
+ if (iface == null) {
+ Log.e(TAG, "Unable to get InterfaceParams for " + ifaceName);
+ return;
+ }
+
+ try {
+ mBpfMap.updateEntry(new U32(iface.index), new InterfaceMapValue(ifaceName));
+ } catch (ErrnoException e) {
+ Log.e(TAG, "Unable to update entry for " + ifaceName + ", " + e);
+ }
+ }
+
+ private class InterfaceChangeObserver extends BaseNetdUnsolicitedEventListener {
+ @Override
+ public void onInterfaceAdded(String ifName) {
+ mHandler.post(() -> addInterface(ifName));
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/net/DelayedDiskWrite.java b/packages/ConnectivityT/service/src/com/android/server/net/DelayedDiskWrite.java
index 8f09eb7c19ab..35dc4557252c 100644
--- a/services/core/java/com/android/server/net/DelayedDiskWrite.java
+++ b/packages/ConnectivityT/service/src/com/android/server/net/DelayedDiskWrite.java
@@ -26,21 +26,37 @@ import java.io.DataOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
+/**
+ * This class provides APIs to do a delayed data write to a given {@link OutputStream}.
+ */
public class DelayedDiskWrite {
+ private static final String TAG = "DelayedDiskWrite";
+
private HandlerThread mDiskWriteHandlerThread;
private Handler mDiskWriteHandler;
/* Tracks multiple writes on the same thread */
private int mWriteSequence = 0;
- private final String TAG = "DelayedDiskWrite";
+ /**
+ * Used to do a delayed data write to a given {@link OutputStream}.
+ */
public interface Writer {
- public void onWriteCalled(DataOutputStream out) throws IOException;
+ /**
+ * write data to a given {@link OutputStream}.
+ */
+ void onWriteCalled(DataOutputStream out) throws IOException;
}
+ /**
+ * Do a delayed data write to a given output stream opened from filePath.
+ */
public void write(final String filePath, final Writer w) {
write(filePath, w, true);
}
+ /**
+ * Do a delayed data write to a given output stream opened from filePath.
+ */
public void write(final String filePath, final Writer w, final boolean open) {
if (TextUtils.isEmpty(filePath)) {
throw new IllegalArgumentException("empty file path");
@@ -77,7 +93,7 @@ public class DelayedDiskWrite {
if (out != null) {
try {
out.close();
- } catch (Exception e) {}
+ } catch (Exception e) { }
}
// Quit if no more writes sent
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/InterfaceMapValue.java b/packages/ConnectivityT/service/src/com/android/server/net/InterfaceMapValue.java
new file mode 100644
index 000000000000..061f323447b0
--- /dev/null
+++ b/packages/ConnectivityT/service/src/com/android/server/net/InterfaceMapValue.java
@@ -0,0 +1,35 @@
+/*
+ * 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.net;
+
+import com.android.net.module.util.Struct;
+import com.android.net.module.util.Struct.Field;
+import com.android.net.module.util.Struct.Type;
+
+/**
+ * The value of bpf interface index map which is used for NetworkStatsService.
+ */
+public class InterfaceMapValue extends Struct {
+ @Field(order = 0, type = Type.ByteArray, arraysize = 16)
+ public final byte[] interfaceName;
+
+ public InterfaceMapValue(String iface) {
+ final byte[] ifaceArray = iface.getBytes();
+ interfaceName = new byte[16];
+ // All array bytes after the interface name, if any, must be 0.
+ System.arraycopy(ifaceArray, 0, interfaceName, 0, ifaceArray.length);
+ }
+}
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
index e15acf3ef616..4c78dcb6269e 100644
--- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
+++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
@@ -19,12 +19,16 @@ package com.android.server.net;
import static android.Manifest.permission.NETWORK_STATS_PROVIDER;
import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY;
import static android.Manifest.permission.UPDATE_DEVICE_STATS;
+import static android.app.usage.NetworkStatsManager.PREFIX_DEV;
+import static android.app.usage.NetworkStatsManager.PREFIX_UID;
+import static android.app.usage.NetworkStatsManager.PREFIX_UID_TAG;
+import static android.app.usage.NetworkStatsManager.PREFIX_XT;
import static android.content.Intent.ACTION_SHUTDOWN;
import static android.content.Intent.ACTION_UID_REMOVED;
import static android.content.Intent.ACTION_USER_REMOVED;
import static android.content.Intent.EXTRA_UID;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static android.net.NetworkIdentity.SUBTYPE_COMBINED;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.net.NetworkStats.DEFAULT_NETWORK_ALL;
import static android.net.NetworkStats.IFACE_ALL;
import static android.net.NetworkStats.IFACE_VT;
@@ -47,23 +51,6 @@ import static android.net.TrafficStats.MB_IN_BYTES;
import static android.net.TrafficStats.UID_TETHERING;
import static android.net.TrafficStats.UNSUPPORTED;
import static android.os.Trace.TRACE_TAG_NETWORK;
-import static android.provider.Settings.Global.NETSTATS_AUGMENT_ENABLED;
-import static android.provider.Settings.Global.NETSTATS_COMBINE_SUBTYPE_ENABLED;
-import static android.provider.Settings.Global.NETSTATS_DEV_BUCKET_DURATION;
-import static android.provider.Settings.Global.NETSTATS_DEV_DELETE_AGE;
-import static android.provider.Settings.Global.NETSTATS_DEV_PERSIST_BYTES;
-import static android.provider.Settings.Global.NETSTATS_DEV_ROTATE_AGE;
-import static android.provider.Settings.Global.NETSTATS_GLOBAL_ALERT_BYTES;
-import static android.provider.Settings.Global.NETSTATS_POLL_INTERVAL;
-import static android.provider.Settings.Global.NETSTATS_SAMPLE_ENABLED;
-import static android.provider.Settings.Global.NETSTATS_UID_BUCKET_DURATION;
-import static android.provider.Settings.Global.NETSTATS_UID_DELETE_AGE;
-import static android.provider.Settings.Global.NETSTATS_UID_PERSIST_BYTES;
-import static android.provider.Settings.Global.NETSTATS_UID_ROTATE_AGE;
-import static android.provider.Settings.Global.NETSTATS_UID_TAG_BUCKET_DURATION;
-import static android.provider.Settings.Global.NETSTATS_UID_TAG_DELETE_AGE;
-import static android.provider.Settings.Global.NETSTATS_UID_TAG_PERSIST_BYTES;
-import static android.provider.Settings.Global.NETSTATS_UID_TAG_ROTATE_AGE;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
import static android.text.format.DateUtils.DAY_IN_MILLIS;
import static android.text.format.DateUtils.HOUR_IN_MILLIS;
@@ -119,7 +106,6 @@ import android.os.Binder;
import android.os.DropBoxManager;
import android.os.Environment;
import android.os.Handler;
-import android.os.HandlerExecutor;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
@@ -154,9 +140,9 @@ import com.android.net.module.util.BaseNetdUnsolicitedEventListener;
import com.android.net.module.util.BestClock;
import com.android.net.module.util.BinderUtils;
import com.android.net.module.util.CollectionUtils;
+import com.android.net.module.util.LocationPermissionChecker;
import com.android.net.module.util.NetworkStatsUtils;
import com.android.net.module.util.PermissionUtils;
-import com.android.server.LocalServices;
import java.io.File;
import java.io.FileDescriptor;
@@ -217,6 +203,10 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
private static final int LOG_TAG_NETSTATS_MOBILE_SAMPLE = 51100;
private static final int LOG_TAG_NETSTATS_WIFI_SAMPLE = 51101;
+ // TODO: Replace the hardcoded string and move it into ConnectivitySettingsManager.
+ private static final String NETSTATS_COMBINE_SUBTYPE_ENABLED =
+ "netstats_combine_subtype_enabled";
+
private final Context mContext;
private final NetworkStatsFactory mStatsFactory;
private final AlarmManager mAlarmManager;
@@ -243,11 +233,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
private PendingIntent mPollIntent;
- private static final String PREFIX_DEV = "dev";
- private static final String PREFIX_XT = "xt";
- private static final String PREFIX_UID = "uid";
- private static final String PREFIX_UID_TAG = "uid_tag";
-
/**
* Settings that can be changed externally.
*/
@@ -257,9 +242,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
boolean getSampleEnabled();
boolean getAugmentEnabled();
/**
- * When enabled, all mobile data is reported under {@link NetworkIdentity#SUBTYPE_COMBINED}.
- * When disabled, mobile data is broken down by a granular subtype representative of the
- * actual subtype. {@see NetworkTemplate#getCollapsedRatType}.
+ * When enabled, all mobile data is reported under {@link NetworkTemplate#NETWORK_TYPE_ALL}.
+ * When disabled, mobile data is broken down by a granular ratType representative of the
+ * actual ratType. {@see NetworkTemplate#getCollapsedRatType}.
* Enabling this decreases the level of detail but saves performance, disk space and
* amount of data logged.
*/
@@ -306,6 +291,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
/** Set of any ifaces associated with mobile networks since boot. */
private volatile String[] mMobileIfaces = new String[0];
+ /** Set of any ifaces associated with wifi networks since boot. */
+ private volatile String[] mWifiIfaces = new String[0];
+
/** Set of all ifaces currently used by traffic that does not explicitly specify a Network. */
@GuardedBy("mStatsLock")
private Network[] mDefaultNetworks = new Network[0];
@@ -366,6 +354,12 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
@NonNull
private final NetworkStatsSubscriptionsMonitor mNetworkStatsSubscriptionsMonitor;
+ @NonNull
+ private final LocationPermissionChecker mLocationPermissionChecker;
+
+ @NonNull
+ private final BpfInterfaceMapUpdater mInterfaceMapUpdater;
+
private static @NonNull File getDefaultSystemDir() {
return new File(Environment.getDataDirectory(), "system");
}
@@ -428,7 +422,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
final NetworkStatsService service = new NetworkStatsService(context,
INetd.Stub.asInterface((IBinder) context.getSystemService(Context.NETD_SERVICE)),
alarmManager, wakeLock, getDefaultClock(),
- new DefaultNetworkStatsSettings(context), new NetworkStatsFactory(netd),
+ new DefaultNetworkStatsSettings(), new NetworkStatsFactory(netd),
new NetworkStatsObservers(), getDefaultSystemDir(), getDefaultBaseDir(),
new Dependencies());
@@ -458,10 +452,13 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
handlerThread.start();
mHandler = new NetworkStatsHandler(handlerThread.getLooper());
mNetworkStatsSubscriptionsMonitor = deps.makeSubscriptionsMonitor(mContext,
- new HandlerExecutor(mHandler), this);
+ (command) -> mHandler.post(command) , this);
mContentResolver = mContext.getContentResolver();
mContentObserver = mDeps.makeContentObserver(mHandler, mSettings,
mNetworkStatsSubscriptionsMonitor);
+ mLocationPermissionChecker = mDeps.makeLocationPermissionChecker(mContext);
+ mInterfaceMapUpdater = mDeps.makeBpfInterfaceMapUpdater(mContext, mHandler);
+ mInterfaceMapUpdater.start();
}
/**
@@ -509,6 +506,20 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
}
};
}
+
+ /**
+ * @see LocationPermissionChecker
+ */
+ public LocationPermissionChecker makeLocationPermissionChecker(final Context context) {
+ return new LocationPermissionChecker(context);
+ }
+
+ /** Create BpfInterfaceMapUpdater to update bpf interface map. */
+ @NonNull
+ public BpfInterfaceMapUpdater makeBpfInterfaceMapUpdater(
+ @NonNull Context ctx, @NonNull Handler handler) {
+ return new BpfInterfaceMapUpdater(ctx, handler);
+ }
}
/**
@@ -557,7 +568,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
// watch for tethering changes
final TetheringManager tetheringManager = mContext.getSystemService(TetheringManager.class);
tetheringManager.registerTetheringEventCallback(
- new HandlerExecutor(mHandler), mTetherListener);
+ (command) -> mHandler.post(command), mTetherListener);
// listen for periodic polling events
final IntentFilter pollFilter = new IntentFilter(ACTION_NETWORK_STATS_POLL);
@@ -591,13 +602,13 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
mSettings.getPollInterval(), pollIntent);
mContentResolver.registerContentObserver(Settings.Global
- .getUriFor(Settings.Global.NETSTATS_COMBINE_SUBTYPE_ENABLED),
+ .getUriFor(NETSTATS_COMBINE_SUBTYPE_ENABLED),
false /* notifyForDescendants */, mContentObserver);
// Post a runnable on handler thread to call onChange(). It's for getting current value of
// NETSTATS_COMBINE_SUBTYPE_ENABLED to decide start or stop monitoring RAT type changes.
mHandler.post(() -> mContentObserver.onChange(false, Settings.Global
- .getUriFor(Settings.Global.NETSTATS_COMBINE_SUBTYPE_ENABLED)));
+ .getUriFor(NETSTATS_COMBINE_SUBTYPE_ENABLED)));
registerGlobalAlert();
}
@@ -708,12 +719,25 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
return now - lastCallTime < POLL_RATE_LIMIT_MS;
}
- private INetworkStatsSession openSessionInternal(final int flags, final String callingPackage) {
+ private int restrictFlagsForCaller(int flags) {
+ // All non-privileged callers are not allowed to turn off POLL_ON_OPEN.
+ final boolean isPrivileged = PermissionUtils.checkAnyPermissionOf(mContext,
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ android.Manifest.permission.NETWORK_STACK);
+ if (!isPrivileged) {
+ flags |= NetworkStatsManager.FLAG_POLL_ON_OPEN;
+ }
+ // Non-system uids are rate limited for POLL_ON_OPEN.
final int callingUid = Binder.getCallingUid();
- final int usedFlags = isRateLimitedForPoll(callingUid)
+ flags = isRateLimitedForPoll(callingUid)
? flags & (~NetworkStatsManager.FLAG_POLL_ON_OPEN)
: flags;
- if ((usedFlags & (NetworkStatsManager.FLAG_POLL_ON_OPEN
+ return flags;
+ }
+
+ private INetworkStatsSession openSessionInternal(final int flags, final String callingPackage) {
+ final int restrictedFlags = restrictFlagsForCaller(flags);
+ if ((restrictedFlags & (NetworkStatsManager.FLAG_POLL_ON_OPEN
| NetworkStatsManager.FLAG_POLL_FORCE)) != 0) {
final long ident = Binder.clearCallingIdentity();
try {
@@ -727,7 +751,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
// for its lifetime; when caller closes only weak references remain.
return new INetworkStatsSession.Stub() {
- private final int mCallingUid = callingUid;
+ private final int mCallingUid = Binder.getCallingUid();
private final String mCallingPackage = callingPackage;
private final @NetworkStatsAccess.Level int mAccessLevel = checkAccessLevel(
callingPackage);
@@ -761,26 +785,41 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
@Override
public NetworkStats getDeviceSummaryForNetwork(
NetworkTemplate template, long start, long end) {
- return internalGetSummaryForNetwork(template, usedFlags, start, end, mAccessLevel,
- mCallingUid);
+ enforceTemplatePermissions(template, callingPackage);
+ return internalGetSummaryForNetwork(template, restrictedFlags, start, end,
+ mAccessLevel, mCallingUid);
}
@Override
public NetworkStats getSummaryForNetwork(
NetworkTemplate template, long start, long end) {
- return internalGetSummaryForNetwork(template, usedFlags, start, end, mAccessLevel,
- mCallingUid);
+ enforceTemplatePermissions(template, callingPackage);
+ return internalGetSummaryForNetwork(template, restrictedFlags, start, end,
+ mAccessLevel, mCallingUid);
}
+ // TODO: Remove this after all callers are removed.
@Override
public NetworkStatsHistory getHistoryForNetwork(NetworkTemplate template, int fields) {
- return internalGetHistoryForNetwork(template, usedFlags, fields, mAccessLevel,
- mCallingUid);
+ enforceTemplatePermissions(template, callingPackage);
+ return internalGetHistoryForNetwork(template, restrictedFlags, fields,
+ mAccessLevel, mCallingUid, Long.MIN_VALUE, Long.MAX_VALUE);
+ }
+
+ @Override
+ public NetworkStatsHistory getHistoryIntervalForNetwork(NetworkTemplate template,
+ int fields, long start, long end) {
+ enforceTemplatePermissions(template, callingPackage);
+ // TODO(b/200768422): Redact returned history if the template is location
+ // sensitive but the caller is not privileged.
+ return internalGetHistoryForNetwork(template, restrictedFlags, fields,
+ mAccessLevel, mCallingUid, start, end);
}
@Override
public NetworkStats getSummaryForAllUid(
NetworkTemplate template, long start, long end, boolean includeTags) {
+ enforceTemplatePermissions(template, callingPackage);
try {
final NetworkStats stats = getUidComplete()
.getSummary(template, start, end, mAccessLevel, mCallingUid);
@@ -798,6 +837,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
@Override
public NetworkStats getTaggedSummaryForAllUid(
NetworkTemplate template, long start, long end) {
+ enforceTemplatePermissions(template, callingPackage);
try {
final NetworkStats tagStats = getUidTagComplete()
.getSummary(template, start, end, mAccessLevel, mCallingUid);
@@ -810,6 +850,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
@Override
public NetworkStatsHistory getHistoryForUid(
NetworkTemplate template, int uid, int set, int tag, int fields) {
+ enforceTemplatePermissions(template, callingPackage);
// NOTE: We don't augment UID-level statistics
if (tag == TAG_NONE) {
return getUidComplete().getHistory(template, null, uid, set, tag, fields,
@@ -824,6 +865,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
public NetworkStatsHistory getHistoryIntervalForUid(
NetworkTemplate template, int uid, int set, int tag, int fields,
long start, long end) {
+ enforceTemplatePermissions(template, callingPackage);
+ // TODO(b/200768422): Redact returned history if the template is location
+ // sensitive but the caller is not privileged.
// NOTE: We don't augment UID-level statistics
if (tag == TAG_NONE) {
return getUidComplete().getHistory(template, null, uid, set, tag, fields,
@@ -845,6 +889,26 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
};
}
+ private void enforceTemplatePermissions(@NonNull NetworkTemplate template,
+ @NonNull String callingPackage) {
+ // For a template with wifi network keys, it is possible for a malicious
+ // client to track the user locations via querying data usage. Thus, enforce
+ // fine location permission check.
+ if (!template.getWifiNetworkKeys().isEmpty()) {
+ final boolean canAccessFineLocation = mLocationPermissionChecker
+ .checkCallersLocationPermission(callingPackage,
+ null /* featureId */,
+ Binder.getCallingUid(),
+ false /* coarseForTargetSdkLessThanQ */,
+ null /* message */);
+ if (!canAccessFineLocation) {
+ throw new SecurityException("Access fine location is required when querying"
+ + " with wifi network keys, make sure the app has the necessary"
+ + "permissions and the location toggle is on.");
+ }
+ }
+ }
+
private @NetworkStatsAccess.Level int checkAccessLevel(String callingPackage) {
return NetworkStatsAccess.checkAccessLevel(
mContext, Binder.getCallingPid(), Binder.getCallingUid(), callingPackage);
@@ -881,7 +945,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
// We've been using pure XT stats long enough that we no longer need to
// splice DEV and XT together.
final NetworkStatsHistory history = internalGetHistoryForNetwork(template, flags, FIELD_ALL,
- accessLevel, callingUid);
+ accessLevel, callingUid, start, end);
final long now = System.currentTimeMillis();
final NetworkStatsHistory.Entry entry = history.getValues(start, end, now, null);
@@ -898,14 +962,14 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
* appropriate.
*/
private NetworkStatsHistory internalGetHistoryForNetwork(NetworkTemplate template,
- int flags, int fields, @NetworkStatsAccess.Level int accessLevel, int callingUid) {
+ int flags, int fields, @NetworkStatsAccess.Level int accessLevel, int callingUid,
+ long start, long end) {
// We've been using pure XT stats long enough that we no longer need to
// splice DEV and XT together.
final SubscriptionPlan augmentPlan = resolveSubscriptionPlan(template, flags);
synchronized (mStatsLock) {
return mXtStatsCached.getHistory(template, augmentPlan,
- UID_ALL, SET_ALL, TAG_NONE, fields, Long.MIN_VALUE, Long.MAX_VALUE,
- accessLevel, callingUid);
+ UID_ALL, SET_ALL, TAG_NONE, fields, start, end, accessLevel, callingUid);
}
}
@@ -956,11 +1020,15 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
}
@Override
- public NetworkStats getDetailedUidStats(String[] requiredIfaces) {
+ public NetworkStats getUidStatsForTransport(int transport) {
enforceAnyPermissionOf(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
try {
+ final String[] relevantIfaces =
+ transport == TRANSPORT_WIFI ? mWifiIfaces : mMobileIfaces;
+ // TODO(b/215633405) : mMobileIfaces and mWifiIfaces already contain the stacked
+ // interfaces, so this is not useful, remove it.
final String[] ifacesToQuery =
- mStatsFactory.augmentWithStackedInterfaces(requiredIfaces);
+ mStatsFactory.augmentWithStackedInterfaces(relevantIfaces);
return getNetworkStatsUidDetail(ifacesToQuery);
} catch (RemoteException e) {
Log.wtf(TAG, "Error compiling UID stats", e);
@@ -1319,16 +1387,18 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
final boolean combineSubtypeEnabled = mSettings.getCombineSubtypeEnabled();
final ArraySet<String> mobileIfaces = new ArraySet<>();
+ final ArraySet<String> wifiIfaces = new ArraySet<>();
for (NetworkStateSnapshot snapshot : snapshots) {
final int displayTransport =
getDisplayTransport(snapshot.getNetworkCapabilities().getTransportTypes());
final boolean isMobile = (NetworkCapabilities.TRANSPORT_CELLULAR == displayTransport);
+ final boolean isWifi = (NetworkCapabilities.TRANSPORT_WIFI == displayTransport);
final boolean isDefault = CollectionUtils.contains(
mDefaultNetworks, snapshot.getNetwork());
- final int subType = combineSubtypeEnabled ? SUBTYPE_COMBINED
- : getSubTypeForStateSnapshot(snapshot);
+ final int ratType = combineSubtypeEnabled ? NetworkTemplate.NETWORK_TYPE_ALL
+ : getRatTypeForStateSnapshot(snapshot);
final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, snapshot,
- isDefault, subType);
+ isDefault, ratType);
// Traffic occurring on the base interface is always counted for
// both total usage and UID details.
@@ -1343,12 +1413,12 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
// VT is considered always metered in framework's layer. If VT is not metered
// per carrier's policy, modem will report 0 usage for VT calls.
if (snapshot.getNetworkCapabilities().hasCapability(
- NetworkCapabilities.NET_CAPABILITY_IMS) && !ident.getMetered()) {
+ NetworkCapabilities.NET_CAPABILITY_IMS) && !ident.isMetered()) {
// Copy the identify from IMS one but mark it as metered.
NetworkIdentity vtIdent = new NetworkIdentity(ident.getType(),
- ident.getSubType(), ident.getSubscriberId(), ident.getNetworkId(),
- ident.getRoaming(), true /* metered */,
+ ident.getRatType(), ident.getSubscriberId(), ident.getWifiNetworkKey(),
+ ident.isRoaming(), true /* metered */,
true /* onDefaultNetwork */, ident.getOemManaged());
final String ifaceVt = IFACE_VT + getSubIdForMobile(snapshot);
findOrCreateNetworkIdentitySet(mActiveIfaces, ifaceVt).add(vtIdent);
@@ -1358,6 +1428,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
if (isMobile) {
mobileIfaces.add(baseIface);
}
+ if (isWifi) {
+ wifiIfaces.add(baseIface);
+ }
}
// Traffic occurring on stacked interfaces is usually clatd.
@@ -1399,6 +1472,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
if (isMobile) {
mobileIfaces.add(iface);
}
+ if (isWifi) {
+ wifiIfaces.add(iface);
+ }
mStatsFactory.noteStackedIface(iface, baseIface);
}
@@ -1406,11 +1482,16 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
}
mMobileIfaces = mobileIfaces.toArray(new String[0]);
+ mWifiIfaces = wifiIfaces.toArray(new String[0]);
// TODO (b/192758557): Remove debug log.
if (CollectionUtils.contains(mMobileIfaces, null)) {
throw new NullPointerException(
"null element in mMobileIfaces: " + Arrays.toString(mMobileIfaces));
}
+ if (CollectionUtils.contains(mWifiIfaces, null)) {
+ throw new NullPointerException(
+ "null element in mWifiIfaces: " + Arrays.toString(mWifiIfaces));
+ }
}
private static int getSubIdForMobile(@NonNull NetworkStateSnapshot state) {
@@ -1428,11 +1509,11 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
}
/**
- * For networks with {@code TRANSPORT_CELLULAR}, get subType that was obtained through
+ * For networks with {@code TRANSPORT_CELLULAR}, get ratType that was obtained through
* {@link PhoneStateListener}. Otherwise, return 0 given that other networks with different
* transport types do not actually fill this value.
*/
- private int getSubTypeForStateSnapshot(@NonNull NetworkStateSnapshot state) {
+ private int getRatTypeForStateSnapshot(@NonNull NetworkStateSnapshot state) {
if (!state.getNetworkCapabilities().hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
return 0;
}
@@ -2120,7 +2201,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
public void notifyWarningOrLimitReached() {
Log.d(TAG, mTag + ": notifyWarningOrLimitReached");
BinderUtils.withCleanCallingIdentity(() ->
- mNetworkPolicyManager.onStatsProviderWarningOrLimitReached());
+ mNetworkPolicyManager.notifyStatsProviderWarningOrLimitReached());
}
@Override
@@ -2179,24 +2260,11 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
* {@link android.provider.Settings.Global}.
*/
private static class DefaultNetworkStatsSettings implements NetworkStatsSettings {
- private final ContentResolver mResolver;
-
- public DefaultNetworkStatsSettings(Context context) {
- mResolver = Objects.requireNonNull(context.getContentResolver());
- // TODO: adjust these timings for production builds
- }
-
- private long getGlobalLong(String name, long def) {
- return Settings.Global.getLong(mResolver, name, def);
- }
- private boolean getGlobalBoolean(String name, boolean def) {
- final int defInt = def ? 1 : 0;
- return Settings.Global.getInt(mResolver, name, defInt) != 0;
- }
+ DefaultNetworkStatsSettings() {}
@Override
public long getPollInterval() {
- return getGlobalLong(NETSTATS_POLL_INTERVAL, 30 * MINUTE_IN_MILLIS);
+ return 30 * MINUTE_IN_MILLIS;
}
@Override
public long getPollDelay() {
@@ -2204,25 +2272,23 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
}
@Override
public long getGlobalAlertBytes(long def) {
- return getGlobalLong(NETSTATS_GLOBAL_ALERT_BYTES, def);
+ return def;
}
@Override
public boolean getSampleEnabled() {
- return getGlobalBoolean(NETSTATS_SAMPLE_ENABLED, true);
+ return true;
}
@Override
public boolean getAugmentEnabled() {
- return getGlobalBoolean(NETSTATS_AUGMENT_ENABLED, true);
+ return true;
}
@Override
public boolean getCombineSubtypeEnabled() {
- return getGlobalBoolean(NETSTATS_COMBINE_SUBTYPE_ENABLED, false);
+ return false;
}
@Override
public Config getDevConfig() {
- return new Config(getGlobalLong(NETSTATS_DEV_BUCKET_DURATION, HOUR_IN_MILLIS),
- getGlobalLong(NETSTATS_DEV_ROTATE_AGE, 15 * DAY_IN_MILLIS),
- getGlobalLong(NETSTATS_DEV_DELETE_AGE, 90 * DAY_IN_MILLIS));
+ return new Config(HOUR_IN_MILLIS, 15 * DAY_IN_MILLIS, 90 * DAY_IN_MILLIS);
}
@Override
public Config getXtConfig() {
@@ -2230,31 +2296,27 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
}
@Override
public Config getUidConfig() {
- return new Config(getGlobalLong(NETSTATS_UID_BUCKET_DURATION, 2 * HOUR_IN_MILLIS),
- getGlobalLong(NETSTATS_UID_ROTATE_AGE, 15 * DAY_IN_MILLIS),
- getGlobalLong(NETSTATS_UID_DELETE_AGE, 90 * DAY_IN_MILLIS));
+ return new Config(2 * HOUR_IN_MILLIS, 15 * DAY_IN_MILLIS, 90 * DAY_IN_MILLIS);
}
@Override
public Config getUidTagConfig() {
- return new Config(getGlobalLong(NETSTATS_UID_TAG_BUCKET_DURATION, 2 * HOUR_IN_MILLIS),
- getGlobalLong(NETSTATS_UID_TAG_ROTATE_AGE, 5 * DAY_IN_MILLIS),
- getGlobalLong(NETSTATS_UID_TAG_DELETE_AGE, 15 * DAY_IN_MILLIS));
+ return new Config(2 * HOUR_IN_MILLIS, 5 * DAY_IN_MILLIS, 15 * DAY_IN_MILLIS);
}
@Override
public long getDevPersistBytes(long def) {
- return getGlobalLong(NETSTATS_DEV_PERSIST_BYTES, def);
+ return def;
}
@Override
public long getXtPersistBytes(long def) {
- return getDevPersistBytes(def);
+ return def;
}
@Override
public long getUidPersistBytes(long def) {
- return getGlobalLong(NETSTATS_UID_PERSIST_BYTES, def);
+ return def;
}
@Override
public long getUidTagPersistBytes(long def) {
- return getGlobalLong(NETSTATS_UID_TAG_PERSIST_BYTES, def);
+ return def;
}
}
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index a3eb0eccad9d..ce58ff6fc59d 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -236,7 +236,8 @@ public class ExternalStorageProvider extends FileSystemProvider {
root.flags |= Root.FLAG_REMOVABLE_USB;
}
- if (volume.getType() != VolumeInfo.TYPE_EMULATED) {
+ if (volume.getType() != VolumeInfo.TYPE_EMULATED
+ && volume.getType() != VolumeInfo.TYPE_STUB) {
root.flags |= Root.FLAG_SUPPORTS_EJECT;
}
diff --git a/packages/SettingsLib/res/values/config.xml b/packages/SettingsLib/res/values/config.xml
index 45253bb7944a..b150e0169a96 100644
--- a/packages/SettingsLib/res/values/config.xml
+++ b/packages/SettingsLib/res/values/config.xml
@@ -28,4 +28,9 @@
<!-- Control whether status bar should distinguish HSPA data icon form UMTS
data icon on devices -->
<bool name="config_hspa_data_distinguishable">false</bool>
+
+ <integer-array name="config_supportedDreamComplications">
+ </integer-array>
+ <integer-array name="config_dreamComplicationsEnabledByDefault">
+ </integer-array>
</resources> \ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/InterestingConfigChanges.java b/packages/SettingsLib/src/com/android/settingslib/applications/InterestingConfigChanges.java
index 5f2bef723cd9..64a0781c4643 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/InterestingConfigChanges.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/InterestingConfigChanges.java
@@ -31,9 +31,8 @@ public class InterestingConfigChanges {
private int mLastDensity;
public InterestingConfigChanges() {
- this(ActivityInfo.CONFIG_LOCALE
- | ActivityInfo.CONFIG_UI_MODE | ActivityInfo.CONFIG_SCREEN_LAYOUT
- | ActivityInfo.CONFIG_ASSETS_PATHS);
+ this(ActivityInfo.CONFIG_LOCALE | ActivityInfo.CONFIG_LAYOUT_DIRECTION
+ | ActivityInfo.CONFIG_UI_MODE | ActivityInfo.CONFIG_ASSETS_PATHS);
}
public InterestingConfigChanges(int flags) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
index 8ac4e38677fa..389892ed15e4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
@@ -170,12 +170,6 @@ public class BluetoothEventManager {
}
@VisibleForTesting
- void registerIntentReceiver() {
- mContext.registerReceiverAsUser(mBroadcastReceiver, mUserHandle, mAdapterIntentFilter,
- null, mReceiverHandler);
- }
-
- @VisibleForTesting
void addProfileHandler(String action, Handler handler) {
mHandlerMap.put(action, handler);
mProfileIntentFilter.addAction(action);
diff --git a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
index aed2ec10e924..7168f3cf1f9c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
+++ b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
@@ -38,6 +38,8 @@ import android.util.AttributeSet;
import android.util.Log;
import android.util.Xml;
+import com.android.settingslib.R;
+
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -45,9 +47,12 @@ import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
public class DreamBackend {
private static final String TAG = "DreamBackend";
@@ -78,19 +83,41 @@ public class DreamBackend {
@Retention(RetentionPolicy.SOURCE)
@IntDef({WHILE_CHARGING, WHILE_DOCKED, EITHER, NEVER})
- public @interface WhenToDream{}
+ public @interface WhenToDream {}
public static final int WHILE_CHARGING = 0;
public static final int WHILE_DOCKED = 1;
public static final int EITHER = 2;
public static final int NEVER = 3;
+ /**
+ * The type of dream complications which can be provided by a
+ * {@link com.android.systemui.dreams.ComplicationProvider}.
+ */
+ @IntDef(prefix = {"COMPLICATION_TYPE_"}, value = {
+ COMPLICATION_TYPE_TIME,
+ COMPLICATION_TYPE_DATE,
+ COMPLICATION_TYPE_WEATHER,
+ COMPLICATION_TYPE_AIR_QUALITY,
+ COMPLICATION_TYPE_CAST_INFO
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ComplicationType {}
+
+ public static final int COMPLICATION_TYPE_TIME = 1;
+ public static final int COMPLICATION_TYPE_DATE = 2;
+ public static final int COMPLICATION_TYPE_WEATHER = 3;
+ public static final int COMPLICATION_TYPE_AIR_QUALITY = 4;
+ public static final int COMPLICATION_TYPE_CAST_INFO = 5;
+
private final Context mContext;
private final IDreamManager mDreamManager;
private final DreamInfoComparator mComparator;
private final boolean mDreamsEnabledByDefault;
private final boolean mDreamsActivatedOnSleepByDefault;
private final boolean mDreamsActivatedOnDockByDefault;
+ private final Set<Integer> mSupportedComplications;
+ private final Set<Integer> mDefaultEnabledComplications;
private static DreamBackend sInstance;
@@ -103,17 +130,31 @@ public class DreamBackend {
public DreamBackend(Context context) {
mContext = context.getApplicationContext();
+ final Resources resources = mContext.getResources();
+
mDreamManager = IDreamManager.Stub.asInterface(
ServiceManager.getService(DreamService.DREAM_SERVICE));
mComparator = new DreamInfoComparator(getDefaultDream());
- mDreamsEnabledByDefault = mContext.getResources()
- .getBoolean(com.android.internal.R.bool.config_dreamsEnabledByDefault);
- mDreamsActivatedOnSleepByDefault = mContext.getResources()
- .getBoolean(com.android.internal.R.bool.config_dreamsActivatedOnSleepByDefault);
- mDreamsActivatedOnDockByDefault = mContext.getResources()
- .getBoolean(com.android.internal.R.bool.config_dreamsActivatedOnDockByDefault);
- mDreamPreviewDefault = mContext.getResources().getDrawable(
+ mDreamsEnabledByDefault = resources.getBoolean(
+ com.android.internal.R.bool.config_dreamsEnabledByDefault);
+ mDreamsActivatedOnSleepByDefault = resources.getBoolean(
+ com.android.internal.R.bool.config_dreamsActivatedOnSleepByDefault);
+ mDreamsActivatedOnDockByDefault = resources.getBoolean(
+ com.android.internal.R.bool.config_dreamsActivatedOnDockByDefault);
+ mDreamPreviewDefault = resources.getDrawable(
com.android.internal.R.drawable.default_dream_preview);
+
+ mSupportedComplications =
+ Arrays.stream(resources.getIntArray(R.array.config_supportedDreamComplications))
+ .boxed()
+ .collect(Collectors.toSet());
+
+ mDefaultEnabledComplications = Arrays.stream(
+ resources.getIntArray(R.array.config_dreamComplicationsEnabledByDefault))
+ .boxed()
+ // A complication can only be enabled by default if it is also supported.
+ .filter(mSupportedComplications::contains)
+ .collect(Collectors.toSet());
}
public List<DreamInfo> getDreamInfos() {
@@ -242,7 +283,57 @@ public class DreamBackend {
default:
break;
}
+ }
+
+ /** Gets all complications which have been enabled by the user. */
+ public Set<Integer> getEnabledComplications() {
+ final String enabledComplications = Settings.Secure.getString(
+ mContext.getContentResolver(),
+ Settings.Secure.SCREENSAVER_ENABLED_COMPLICATIONS);
+
+ if (enabledComplications == null) {
+ return mDefaultEnabledComplications;
+ }
+
+ return parseFromString(enabledComplications);
+ }
+
+ /** Gets all dream complications which are supported on this device. **/
+ public Set<Integer> getSupportedComplications() {
+ return mSupportedComplications;
+ }
+
+ /**
+ * Enables or disables a particular dream complication.
+ *
+ * @param complicationType The dream complication to be enabled/disabled.
+ * @param value If true, the complication is enabled. Otherwise it is disabled.
+ */
+ public void setComplicationEnabled(@ComplicationType int complicationType, boolean value) {
+ if (!mSupportedComplications.contains(complicationType)) return;
+
+ Set<Integer> enabledComplications = getEnabledComplications();
+ if (value) {
+ enabledComplications.add(complicationType);
+ } else {
+ enabledComplications.remove(complicationType);
+ }
+
+ Settings.Secure.putString(mContext.getContentResolver(),
+ Settings.Secure.SCREENSAVER_ENABLED_COMPLICATIONS,
+ convertToString(enabledComplications));
+ }
+
+ private static String convertToString(Set<Integer> set) {
+ return set.stream()
+ .map(String::valueOf)
+ .collect(Collectors.joining(","));
+ }
+ private static Set<Integer> parseFromString(String string) {
+ return Arrays.stream(string.split(","))
+ .map(Integer::parseInt)
+ .collect(Collectors.toSet());
}
public boolean isEnabled() {
@@ -311,7 +402,10 @@ public class DreamBackend {
if (dreamInfo == null || dreamInfo.settingsComponentName == null) {
return;
}
- uiContext.startActivity(new Intent().setComponent(dreamInfo.settingsComponentName));
+ final Intent intent = new Intent()
+ .setComponent(dreamInfo.settingsComponentName)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ uiContext.startActivity(intent);
}
public void preview(DreamInfo dreamInfo) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java
index 3e95b01824cc..5e9ac5a59091 100644
--- a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java
@@ -16,23 +16,16 @@
package com.android.settingslib.net;
-import static android.net.NetworkStatsHistory.FIELD_RX_BYTES;
-import static android.net.NetworkStatsHistory.FIELD_TX_BYTES;
-
+import android.annotation.NonNull;
import android.app.usage.NetworkStats;
import android.app.usage.NetworkStatsManager;
import android.content.Context;
-import android.net.INetworkStatsService;
-import android.net.INetworkStatsSession;
import android.net.NetworkPolicy;
import android.net.NetworkPolicyManager;
-import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
-import android.net.TrafficStats;
-import android.os.RemoteException;
-import android.os.ServiceManager;
import android.text.format.DateUtils;
import android.util.Pair;
+import android.util.Range;
import androidx.annotation.VisibleForTesting;
import androidx.loader.content.AsyncTaskLoader;
@@ -52,8 +45,6 @@ public abstract class NetworkCycleDataLoader<D> extends AsyncTaskLoader<D> {
protected final NetworkTemplate mNetworkTemplate;
private final NetworkPolicy mPolicy;
private final ArrayList<Long> mCycles;
- @VisibleForTesting
- final INetworkStatsService mNetworkStatsService;
protected NetworkCycleDataLoader(Builder<?> builder) {
super(builder.mContext);
@@ -61,8 +52,6 @@ public abstract class NetworkCycleDataLoader<D> extends AsyncTaskLoader<D> {
mCycles = builder.mCycles;
mNetworkStatsManager = (NetworkStatsManager)
builder.mContext.getSystemService(Context.NETWORK_STATS_SERVICE);
- mNetworkStatsService = INetworkStatsService.Stub.asInterface(
- ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
final NetworkPolicyEditor policyEditor =
new NetworkPolicyEditor(NetworkPolicyManager.from(builder.mContext));
policyEditor.read();
@@ -112,23 +101,20 @@ public abstract class NetworkCycleDataLoader<D> extends AsyncTaskLoader<D> {
@VisibleForTesting
void loadFourWeeksData() {
+ if (mNetworkTemplate == null) return;
+ final NetworkStats stats = mNetworkStatsManager.queryDetailsForDevice(
+ mNetworkTemplate, Long.MIN_VALUE, Long.MAX_VALUE);
try {
- final INetworkStatsSession networkSession = mNetworkStatsService.openSession();
- final NetworkStatsHistory networkHistory = networkSession.getHistoryForNetwork(
- mNetworkTemplate, FIELD_RX_BYTES | FIELD_TX_BYTES);
- final long historyStart = networkHistory.getStart();
- final long historyEnd = networkHistory.getEnd();
-
- long cycleEnd = historyEnd;
- while (cycleEnd > historyStart) {
+ final Range<Long> historyTimeRange = getTimeRangeOf(stats);
+
+ long cycleEnd = historyTimeRange.getUpper();
+ while (cycleEnd > historyTimeRange.getLower()) {
final long cycleStart = cycleEnd - (DateUtils.WEEK_IN_MILLIS * 4);
recordUsage(cycleStart, cycleEnd);
cycleEnd = cycleStart;
}
-
- TrafficStats.closeQuietly(networkSession);
- } catch (RemoteException e) {
- throw new RuntimeException(e);
+ } catch (IllegalArgumentException e) {
+ // Empty history, ignore.
}
}
@@ -169,6 +155,32 @@ public abstract class NetworkCycleDataLoader<D> extends AsyncTaskLoader<D> {
return bytes;
}
+ @NonNull
+ @VisibleForTesting
+ Range getTimeRangeOf(@NonNull NetworkStats stats) {
+ long start = Long.MAX_VALUE;
+ long end = Long.MIN_VALUE;
+ while (hasNextBucket(stats)) {
+ final NetworkStats.Bucket bucket = getNextBucket(stats);
+ start = Math.min(start, bucket.getStartTimeStamp());
+ end = Math.max(end, bucket.getEndTimeStamp());
+ }
+ return new Range(start, end);
+ }
+
+ @VisibleForTesting
+ boolean hasNextBucket(@NonNull NetworkStats stats) {
+ return stats.hasNextBucket();
+ }
+
+ @NonNull
+ @VisibleForTesting
+ NetworkStats.Bucket getNextBucket(@NonNull NetworkStats stats) {
+ NetworkStats.Bucket bucket = new NetworkStats.Bucket();
+ stats.getNextBucket(bucket);
+ return bucket;
+ }
+
@VisibleForTesting(otherwise = VisibleForTesting.NONE)
public ArrayList<Long> getCycles() {
return mCycles;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java
index 63153f89ad99..10ccd22eca83 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java
@@ -708,11 +708,11 @@ public class ApplicationsStateRoboTest {
throws RemoteException {
if (ownerApps != null) {
- when(mApplicationsState.mIpm.getInstalledApplications(anyInt(), eq(0)))
+ when(mApplicationsState.mIpm.getInstalledApplications(anyLong(), eq(0)))
.thenReturn(new ParceledListSlice<>(ownerApps));
}
if (profileApps != null) {
- when(mApplicationsState.mIpm.getInstalledApplications(anyInt(), eq(PROFILE_USERID)))
+ when(mApplicationsState.mIpm.getInstalledApplications(anyLong(), eq(PROFILE_USERID)))
.thenReturn(new ParceledListSlice<>(profileApps));
}
final InterestingConfigChanges configChanges = mock(InterestingConfigChanges.class);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
index bee466d39c23..852ac5ca6abe 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
@@ -129,7 +129,6 @@ public class BluetoothEventManagerTest {
@Test
public void intentWithExtraState_audioStateChangedShouldDispatchToRegisterCallback() {
mBluetoothEventManager.registerCallback(mBluetoothCallback);
- mBluetoothEventManager.registerIntentReceiver();
mIntent = new Intent(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
mContext.sendBroadcast(mIntent);
@@ -143,7 +142,6 @@ public class BluetoothEventManagerTest {
@Test
public void intentWithExtraState_phoneStateChangedShouldDispatchToRegisterCallback() {
mBluetoothEventManager.registerCallback(mBluetoothCallback);
- mBluetoothEventManager.registerIntentReceiver();
mIntent = new Intent(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
mContext.sendBroadcast(mIntent);
@@ -169,7 +167,6 @@ public class BluetoothEventManagerTest {
@Test
public void dispatchAclConnectionStateChanged_aclDisconnected_shouldDispatchCallback() {
mBluetoothEventManager.registerCallback(mBluetoothCallback);
- mBluetoothEventManager.registerIntentReceiver();
mIntent = new Intent(BluetoothDevice.ACTION_ACL_DISCONNECTED);
mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
@@ -182,7 +179,6 @@ public class BluetoothEventManagerTest {
@Test
public void dispatchAclConnectionStateChanged_aclConnected_shouldDispatchCallback() {
mBluetoothEventManager.registerCallback(mBluetoothCallback);
- mBluetoothEventManager.registerIntentReceiver();
mIntent = new Intent(BluetoothDevice.ACTION_ACL_CONNECTED);
mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
@@ -196,7 +192,6 @@ public class BluetoothEventManagerTest {
public void dispatchAclConnectionStateChanged_aclDisconnected_shouldNotCallbackSubDevice() {
when(mCachedDeviceManager.isSubDevice(mBluetoothDevice)).thenReturn(true);
mBluetoothEventManager.registerCallback(mBluetoothCallback);
- mBluetoothEventManager.registerIntentReceiver();
mIntent = new Intent(BluetoothDevice.ACTION_ACL_DISCONNECTED);
mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
@@ -210,7 +205,6 @@ public class BluetoothEventManagerTest {
public void dispatchAclConnectionStateChanged_aclConnected_shouldNotCallbackSubDevice() {
when(mCachedDeviceManager.isSubDevice(mBluetoothDevice)).thenReturn(true);
mBluetoothEventManager.registerCallback(mBluetoothCallback);
- mBluetoothEventManager.registerIntentReceiver();
mIntent = new Intent(BluetoothDevice.ACTION_ACL_CONNECTED);
mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
@@ -224,7 +218,6 @@ public class BluetoothEventManagerTest {
public void dispatchAclConnectionStateChanged_findDeviceReturnNull_shouldNotDispatchCallback() {
when(mCachedDeviceManager.findDevice(mBluetoothDevice)).thenReturn(null);
mBluetoothEventManager.registerCallback(mBluetoothCallback);
- mBluetoothEventManager.registerIntentReceiver();
mIntent = new Intent(BluetoothDevice.ACTION_ACL_CONNECTED);
mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
@@ -361,7 +354,6 @@ public class BluetoothEventManagerTest {
@Test
public void showUnbondMessage_reasonAuthTimeout_showCorrectedErrorCode() {
- mBluetoothEventManager.registerIntentReceiver();
mIntent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
mIntent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE);
@@ -377,7 +369,6 @@ public class BluetoothEventManagerTest {
@Test
public void showUnbondMessage_reasonRemoteDeviceDown_showCorrectedErrorCode() {
- mBluetoothEventManager.registerIntentReceiver();
mIntent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
mIntent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE);
@@ -394,7 +385,6 @@ public class BluetoothEventManagerTest {
@Test
public void showUnbondMessage_reasonAuthRejected_showCorrectedErrorCode() {
- mBluetoothEventManager.registerIntentReceiver();
mIntent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
mIntent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE);
@@ -410,7 +400,6 @@ public class BluetoothEventManagerTest {
@Test
public void showUnbondMessage_reasonAuthFailed_showCorrectedErrorCode() {
- mBluetoothEventManager.registerIntentReceiver();
mIntent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
mIntent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ConnectivityPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ConnectivityPreferenceControllerTest.java
index 4444e6369b67..c1cc3ae9778a 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ConnectivityPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ConnectivityPreferenceControllerTest.java
@@ -33,6 +33,7 @@ import android.os.Handler;
import com.android.settingslib.core.lifecycle.Lifecycle;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -54,6 +55,7 @@ public class ConnectivityPreferenceControllerTest {
}
@Test
+ @Ignore
public void testBroadcastReceiver() {
final AbstractConnectivityPreferenceController preferenceController =
spy(new ConcreteConnectivityPreferenceController(mContext, mLifecycle));
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/dream/DreamBackendTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/dream/DreamBackendTest.java
new file mode 100644
index 000000000000..53d465305a69
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/dream/DreamBackendTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settingslib.dream;
+
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.res.Resources;
+
+import com.android.settingslib.R;
+
+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 org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowSettings;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowSettings.ShadowSecure.class})
+public final class DreamBackendTest {
+ private static final int[] SUPPORTED_DREAM_COMPLICATIONS = {1, 2, 3};
+ private static final int[] DEFAULT_DREAM_COMPLICATIONS = {1, 3, 4};
+
+ @Mock
+ private Context mContext;
+ private DreamBackend mBackend;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ when(mContext.getApplicationContext()).thenReturn(mContext);
+
+ final Resources res = mock(Resources.class);
+ when(mContext.getResources()).thenReturn(res);
+ when(res.getIntArray(R.array.config_supportedDreamComplications)).thenReturn(
+ SUPPORTED_DREAM_COMPLICATIONS);
+ when(res.getIntArray(R.array.config_dreamComplicationsEnabledByDefault)).thenReturn(
+ DEFAULT_DREAM_COMPLICATIONS);
+ mBackend = new DreamBackend(mContext);
+ }
+
+ @After
+ public void tearDown() {
+ ShadowSettings.ShadowSecure.reset();
+ }
+
+ @Test
+ public void testSupportedComplications() {
+ assertThat(mBackend.getSupportedComplications()).containsExactly(1, 2, 3);
+ }
+
+ @Test
+ public void testGetEnabledDreamComplications_default() {
+ assertThat(mBackend.getEnabledComplications()).containsExactly(1, 3);
+ }
+
+ @Test
+ public void testEnableComplication() {
+ mBackend.setComplicationEnabled(/* complicationType= */ 2, true);
+ assertThat(mBackend.getEnabledComplications()).containsExactly(1, 2, 3);
+ }
+
+ @Test
+ public void testEnableComplication_notSupported() {
+ mBackend.setComplicationEnabled(/* complicationType= */ 5, true);
+ assertThat(mBackend.getEnabledComplications()).containsExactly(1, 3);
+ }
+
+ @Test
+ public void testDisableComplication() {
+ mBackend.setComplicationEnabled(/* complicationType= */ 1, false);
+ assertThat(mBackend.getEnabledComplications()).containsExactly(3);
+ }
+}
+
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminControllerTest.java
index 8ec577e8a764..06b6fc8ef73d 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminControllerTest.java
@@ -22,7 +22,6 @@ import static com.android.settingslib.enterprise.FakeDeviceAdminStringProvider.D
import static junit.framework.Assert.assertNotNull;
import static junit.framework.TestCase.assertEquals;
-import static junit.framework.TestCase.assertSame;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
@@ -67,7 +66,7 @@ public class BiometricActionDisabledByAdminControllerTest {
@Test
public void buttonClicked() {
- ComponentName componentName = mock(ComponentName.class);
+ ComponentName componentName = new ComponentName("com.android.test", "AThing");
RestrictedLockUtils.EnforcedAdmin enforcedAdmin = new RestrictedLockUtils.EnforcedAdmin(
componentName, new UserHandle(UserHandle.myUserId()));
@@ -83,6 +82,6 @@ public class BiometricActionDisabledByAdminControllerTest {
assertEquals(Settings.SUPERVISOR_VERIFICATION_SETTING_BIOMETRICS,
intentCaptor.getValue().getStringExtra(
Settings.EXTRA_SUPERVISOR_RESTRICTED_SETTING_KEY));
- assertSame(componentName, intentCaptor.getValue().getComponent());
+ assertEquals(componentName.getPackageName(), intentCaptor.getValue().getPackage());
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
index 2d53831a30e7..aa0ef91be46b 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
@@ -46,6 +46,7 @@ import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.testutils.shadow.ShadowRouter2Manager;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -731,6 +732,7 @@ public class InfoMediaManagerTest {
}
@Test
+ @Ignore
public void shouldDisableMediaOutput_infosSizeEqual1_returnsTrue() {
final MediaRoute2Info info = mock(MediaRoute2Info.class);
final List<MediaRoute2Info> infos = new ArrayList<>();
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java
index 74b91510cf3f..c79440e58e17 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java
@@ -16,24 +16,24 @@
package com.android.settingslib.net;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.nullable;
+import static android.app.usage.NetworkStats.Bucket.UID_ALL;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.annotation.NonNull;
+import android.app.usage.NetworkStats;
import android.app.usage.NetworkStatsManager;
import android.content.Context;
import android.net.ConnectivityManager;
-import android.net.INetworkStatsService;
-import android.net.INetworkStatsSession;
import android.net.NetworkPolicy;
import android.net.NetworkPolicyManager;
-import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
-import android.os.RemoteException;
import android.text.format.DateUtils;
import android.util.Range;
@@ -49,6 +49,8 @@ import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
+import java.util.Queue;
+import java.util.concurrent.LinkedBlockingQueue;
@RunWith(RobolectricTestRunner.class)
public class NetworkCycleDataLoaderTest {
@@ -63,8 +65,6 @@ public class NetworkCycleDataLoaderTest {
private NetworkPolicy mPolicy;
@Mock
private Iterator<Range<ZonedDateTime>> mIterator;
- @Mock
- private INetworkStatsService mNetworkStatsService;
private NetworkCycleDataTestLoader mLoader;
@@ -132,20 +132,24 @@ public class NetworkCycleDataLoaderTest {
verify(mLoader).recordUsage(nowInMs, nowInMs);
}
+ private NetworkStats.Bucket makeMockBucket(int uid, long rxBytes, long txBytes,
+ long start, long end) {
+ NetworkStats.Bucket ret = mock(NetworkStats.Bucket.class);
+ when(ret.getUid()).thenReturn(uid);
+ when(ret.getRxBytes()).thenReturn(rxBytes);
+ when(ret.getTxBytes()).thenReturn(txBytes);
+ when(ret.getStartTimeStamp()).thenReturn(start);
+ when(ret.getEndTimeStamp()).thenReturn(end);
+ return ret;
+ }
+
@Test
- public void loadFourWeeksData_shouldRecordUsageForLast4Weeks() throws RemoteException {
+ public void loadFourWeeksData_shouldRecordUsageForLast4Weeks() {
mLoader = spy(new NetworkCycleDataTestLoader(mContext));
- ReflectionHelpers.setField(mLoader, "mNetworkStatsService", mNetworkStatsService);
- final INetworkStatsSession networkSession = mock(INetworkStatsSession.class);
- when(mNetworkStatsService.openSession()).thenReturn(networkSession);
- final NetworkStatsHistory networkHistory = mock(NetworkStatsHistory.class);
- when(networkSession.getHistoryForNetwork(nullable(NetworkTemplate.class), anyInt()))
- .thenReturn(networkHistory);
final long now = System.currentTimeMillis();
final long fourWeeksAgo = now - (DateUtils.WEEK_IN_MILLIS * 4);
final long twoDaysAgo = now - (DateUtils.DAY_IN_MILLIS * 2);
- when(networkHistory.getStart()).thenReturn(twoDaysAgo);
- when(networkHistory.getEnd()).thenReturn(now);
+ mLoader.addBucket(makeMockBucket(UID_ALL, 123, 456, twoDaysAgo, now));
mLoader.loadFourWeeksData();
@@ -173,10 +177,31 @@ public class NetworkCycleDataLoaderTest {
verify(mLoader).recordUsage(thirtyDaysAgo, twentyDaysAgo);
}
+ @Test
+ public void getTimeRangeOf() {
+ mLoader = spy(new NetworkCycleDataTestLoader(mContext));
+ // If empty, new Range(MAX_VALUE, MIN_VALUE) will be constructed. Hence, the function
+ // should throw.
+ assertThrows(IllegalArgumentException.class,
+ () -> mLoader.getTimeRangeOf(mock(NetworkStats.class)));
+
+ mLoader.addBucket(makeMockBucket(UID_ALL, 123, 456, 0, 10));
+ // Feed the function with unused NetworkStats. The actual data injection is
+ // done by addBucket.
+ assertEquals(new Range(0L, 10L), mLoader.getTimeRangeOf(mock(NetworkStats.class)));
+
+ mLoader.addBucket(makeMockBucket(UID_ALL, 123, 456, 0, 10));
+ mLoader.addBucket(makeMockBucket(UID_ALL, 123, 456, 30, 40));
+ mLoader.addBucket(makeMockBucket(UID_ALL, 123, 456, 10, 25));
+ assertEquals(new Range(0L, 40L), mLoader.getTimeRangeOf(mock(NetworkStats.class)));
+ }
+
public class NetworkCycleDataTestLoader extends NetworkCycleDataLoader<List<NetworkCycleData>> {
+ private final Queue<NetworkStats.Bucket> mMockedBuckets = new LinkedBlockingQueue<>();
private NetworkCycleDataTestLoader(Context context) {
- super(NetworkCycleDataLoader.builder(mContext));
+ super(NetworkCycleDataLoader.builder(mContext)
+ .setNetworkTemplate(mock(NetworkTemplate.class)));
mContext = context;
}
@@ -188,5 +213,19 @@ public class NetworkCycleDataLoaderTest {
List<NetworkCycleData> getCycleUsage() {
return null;
}
+
+ public void addBucket(NetworkStats.Bucket bucket) {
+ mMockedBuckets.add(bucket);
+ }
+
+ @Override
+ public boolean hasNextBucket(@NonNull NetworkStats unused) {
+ return !mMockedBuckets.isEmpty();
+ }
+
+ @Override
+ public NetworkStats.Bucket getNextBucket(@NonNull NetworkStats unused) {
+ return mMockedBuckets.remove();
+ }
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/AccessPointPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/AccessPointPreferenceTest.java
index 2bd20a933c58..30267f793cd6 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/AccessPointPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/AccessPointPreferenceTest.java
@@ -19,11 +19,15 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.graphics.drawable.ColorDrawable;
+import android.net.wifi.WifiManager;
+
+import androidx.test.core.app.ApplicationProvider;
import org.junit.Before;
import org.junit.Test;
@@ -36,7 +40,7 @@ import org.robolectric.RuntimeEnvironment;
@RunWith(RobolectricTestRunner.class)
public class AccessPointPreferenceTest {
- private Context mContext = RuntimeEnvironment.application;
+ private Context mContext;
@Mock
private AccessPoint mockAccessPoint;
@@ -53,6 +57,8 @@ public class AccessPointPreferenceTest {
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+ mContext = spy(ApplicationProvider.getApplicationContext());
+ when(mContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mock(WifiManager.class));
when(mockIconInjector.getIcon(anyInt())).thenReturn(new ColorDrawable());
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java
index 7c2b904fc576..e7b3fe9ab8da 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java
@@ -20,6 +20,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -32,6 +33,7 @@ import android.net.ScoredNetwork;
import android.net.WifiKey;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiManager;
import android.net.wifi.WifiNetworkScoreCache;
import android.os.Bundle;
import android.os.Parcelable;
@@ -74,6 +76,7 @@ public class WifiUtilsTest {
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(ApplicationProvider.getApplicationContext());
+ when(mContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mock(WifiManager.class));
}
@Test
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 231252502937..5f549fd05e1a 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -296,6 +296,7 @@ public class SecureSettingsValidators {
VALIDATORS.put(Secure.CLIPBOARD_SHOW_ACCESS_NOTIFICATIONS, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.NOTIFICATION_BUBBLES, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.LOCATION_TIME_ZONE_DETECTION_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.LOCATION_SHOW_SYSTEM_OPS, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.DEVICE_STATE_ROTATION_LOCK, value -> {
if (TextUtils.isEmpty(value)) {
return true;
@@ -324,6 +325,7 @@ public class SecureSettingsValidators {
return true;
});
VALIDATORS.put(Secure.ODI_CAPTIONS_VOLUME_UI_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.FAST_PAIR_SCAN_ENABLED, BOOLEAN_VALIDATOR);
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index c5f027b829d9..7381e05972d4 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1378,9 +1378,6 @@ class SettingsProtoDumpUtil {
Settings.Global.SYS_STORAGE_CACHE_PERCENTAGE,
GlobalSettingsProto.Sys.STORAGE_CACHE_PERCENTAGE);
dumpSetting(s, p,
- Settings.Global.SYS_STORAGE_CACHE_MAX_BYTES,
- GlobalSettingsProto.Sys.STORAGE_CACHE_MAX_BYTES);
- dumpSetting(s, p,
Settings.Global.SYS_UIDCPUPOWER,
GlobalSettingsProto.Sys.UIDCPUPOWER);
p.end(sysToken);
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index a3f39959e7cf..720fb6ceb131 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -462,7 +462,6 @@ public class SettingsBackupTest {
Settings.Global.SYNC_MANAGER_CONSTANTS,
Settings.Global.SYNC_MAX_RETRY_DELAY_IN_SECONDS,
Settings.Global.SYS_FREE_STORAGE_LOG_INTERVAL,
- Settings.Global.SYS_STORAGE_CACHE_MAX_BYTES,
Settings.Global.SYS_STORAGE_CACHE_PERCENTAGE,
Settings.Global.SYS_STORAGE_FULL_THRESHOLD_BYTES,
Settings.Global.SYS_STORAGE_THRESHOLD_MAX_BYTES,
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index c1a812f5ebb8..46e24faed5b9 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -250,6 +250,7 @@
<uses-permission android:name="android.permission.MANAGE_APP_PREDICTIONS" />
<uses-permission android:name="android.permission.MANAGE_SEARCH_UI" />
<uses-permission android:name="android.permission.MANAGE_SMARTSPACE" />
+ <uses-permission android:name="android.permission.MANAGE_WALLPAPER_EFFECTS_GENERATION" />
<uses-permission android:name="android.permission.MANAGE_UI_TRANSLATION" />
<uses-permission android:name="android.permission.NETWORK_SETTINGS" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
@@ -548,6 +549,10 @@
<!-- Permission required for CTS test - PeopleManagerTest -->
<uses-permission android:name="android.permission.READ_PEOPLE_DATA" />
+ <!-- Permissions required for CTS test - TrustTestCases -->
+ <uses-permission android:name="android.permission.PROVIDE_TRUST_AGENT" />
+ <uses-permission android:name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE" />
+
<!-- Permission required for CTS test - CtsGameManagerTestCases -->
<uses-permission android:name="android.permission.MANAGE_GAME_MODE" />
@@ -623,10 +628,6 @@
<!-- Permission required for CTS test - Notification test suite -->
<uses-permission android:name="android.permission.REVOKE_POST_NOTIFICATIONS_WITHOUT_KILL" />
- <!-- Permission required for CTS test - CommunalManagerTest -->
- <uses-permission android:name="android.permission.WRITE_COMMUNAL_STATE" />
- <uses-permission android:name="android.permission.READ_COMMUNAL_STATE" />
-
<!-- Permission required for CTS test - CaptioningManagerTest -->
<uses-permission android:name="android.permission.SET_SYSTEM_AUDIO_CAPTION" />
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 92ae92e99747..f35f5dd0c3ae 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -153,6 +153,9 @@
<!-- Needed for WallpaperManager.clear in ImageWallpaper.updateWallpaperLocked -->
<uses-permission android:name="android.permission.SET_WALLPAPER"/>
+ <!-- Needed for WallpaperManager.getWallpaperDimAmount in StatusBar.updateTheme -->
+ <uses-permission android:name="android.permission.SET_WALLPAPER_DIM_AMOUNT"/>
+
<!-- Wifi Display -->
<uses-permission android:name="android.permission.CONFIGURE_WIFI_DISPLAY" />
diff --git a/packages/SystemUI/OWNERS b/packages/SystemUI/OWNERS
index e1da74466b55..3ae85e7e2325 100644
--- a/packages/SystemUI/OWNERS
+++ b/packages/SystemUI/OWNERS
@@ -11,8 +11,10 @@ asc@google.com
awickham@google.com
beverlyt@google.com
brockman@google.com
+brzezinski@google.com
brycelee@google.com
ccassidy@google.com
+chrisgollner@google.com
cinek@google.com
cwren@google.com
dupin@google.com
@@ -43,6 +45,8 @@ mpietal@google.com
mrcasey@google.com
mrenouf@google.com
nesciosquid@google.com
+nickchameyev@google.com
+nicomazz@google.com
ogunwale@google.com
peanutbutter@google.com
pinyaoting@google.com
diff --git a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
index da9a92a1f6b4..68c8c3e02a42 100644
--- a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
+++ b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
@@ -122,6 +122,11 @@ public interface BcSmartspaceDataPlugin extends Plugin {
* Set or clear device media playing
*/
void setMediaTarget(@Nullable SmartspaceTarget target);
+
+ /**
+ * Get the index of the currently selected page.
+ */
+ int getSelectedPage();
}
/** Interface for launching Intents, which can differ on the lockscreen */
diff --git a/packages/SystemUI/res-keyguard/layout/footer_actions.xml b/packages/SystemUI/res-keyguard/layout/footer_actions.xml
index dfc3e63a4e2b..ecb3cb3961a4 100644
--- a/packages/SystemUI/res-keyguard/layout/footer_actions.xml
+++ b/packages/SystemUI/res-keyguard/layout/footer_actions.xml
@@ -22,21 +22,6 @@
android:layout_height="48dp"
android:gravity="center_vertical">
- <com.android.systemui.statusbar.AlphaOptimizedImageView
- android:id="@android:id/edit"
- android:layout_width="0dp"
- android:layout_height="@dimen/qs_footer_action_button_size"
- android:layout_marginEnd="@dimen/qs_tile_margin_horizontal"
- android:layout_weight="1"
- android:background="@drawable/qs_footer_action_chip_background"
- android:clickable="true"
- android:clipToPadding="false"
- android:contentDescription="@string/accessibility_quick_settings_edit"
- android:focusable="true"
- android:padding="@dimen/qs_footer_icon_padding"
- android:src="@*android:drawable/ic_mode_edit"
- android:tint="?android:attr/textColorPrimary" />
-
<com.android.systemui.statusbar.phone.MultiUserSwitch
android:id="@+id/multi_user_switch"
android:layout_width="0dp"
diff --git a/packages/SystemUI/res-keyguard/values/bools.xml b/packages/SystemUI/res-keyguard/values/bools.xml
index c5bf4ce48188..2b83787172d3 100644
--- a/packages/SystemUI/res-keyguard/values/bools.xml
+++ b/packages/SystemUI/res-keyguard/values/bools.xml
@@ -17,5 +17,4 @@
<resources>
<bool name="kg_show_ime_at_screen_on">true</bool>
<bool name="kg_use_all_caps">true</bool>
- <bool name="flag_active_unlock">false</bool>
</resources>
diff --git a/packages/SystemUI/res/drawable/ic_warning.xml b/packages/SystemUI/res/drawable/ic_warning.xml
new file mode 100644
index 000000000000..fbed779ec70f
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_warning.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
+ <path android:fillColor="@android:color/white" android:pathData="M12,12.5zM1,21L12,2l11,19zM11,15h2v-5h-2zM12,18q0.425,0 0.713,-0.288Q13,17.425 13,17t-0.287,-0.712Q12.425,16 12,16t-0.713,0.288Q11,16.575 11,17t0.287,0.712Q11.575,18 12,18zM4.45,19h15.1L12,6z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/qs_customizer_background_transition.xml b/packages/SystemUI/res/drawable/qs_customizer_background_transition.xml
index ed8f61a97c2a..6fa9eac2fc29 100644
--- a/packages/SystemUI/res/drawable/qs_customizer_background_transition.xml
+++ b/packages/SystemUI/res/drawable/qs_customizer_background_transition.xml
@@ -15,7 +15,7 @@
-->
<inset xmlns:android="http://schemas.android.com/apk/res/android">
<shape>
- <solid android:color="@color/qs_detail_transition"/>
+ <solid android:color="@android:color/transparent"/>
<corners android:radius="?android:attr/dialogCornerRadius" />
</shape>
</inset>
diff --git a/packages/SystemUI/res/drawable/qs_detail_background.xml b/packages/SystemUI/res/drawable/qs_detail_background.xml
index e5c7352807f8..c23649d629bd 100644
--- a/packages/SystemUI/res/drawable/qs_detail_background.xml
+++ b/packages/SystemUI/res/drawable/qs_detail_background.xml
@@ -17,7 +17,7 @@ Copyright (C) 2014 The Android Open Source Project
<item>
<inset>
<shape>
- <solid android:color="@color/qs_detail_transition"/>
+ <solid android:color="@android:color/transparent"/>
<corners android:radius="@dimen/qs_footer_action_corner_radius" />
</shape>
</inset>
diff --git a/packages/SystemUI/res/layout/media_ttt_chip.xml b/packages/SystemUI/res/layout/media_ttt_chip.xml
index 2d082dc7d5e2..a5fdcd9e2671 100644
--- a/packages/SystemUI/res/layout/media_ttt_chip.xml
+++ b/packages/SystemUI/res/layout/media_ttt_chip.xml
@@ -28,8 +28,8 @@
<com.android.internal.widget.CachingIconView
android:id="@+id/app_icon"
- android:layout_width="@dimen/media_ttt_icon_size"
- android:layout_height="@dimen/media_ttt_icon_size"
+ android:layout_width="@dimen/media_ttt_app_icon_size"
+ android:layout_height="@dimen/media_ttt_app_icon_size"
android:layout_marginEnd="12dp"
/>
@@ -41,23 +41,34 @@
android:textColor="?android:attr/textColorPrimary"
/>
+ <!-- At most one of [loading, failure_icon, undo] will be visible at a time. -->
+
<ProgressBar
android:id="@+id/loading"
android:indeterminate="true"
- android:layout_width="@dimen/media_ttt_loading_size"
- android:layout_height="@dimen/media_ttt_loading_size"
- android:layout_marginStart="12dp"
+ android:layout_width="@dimen/media_ttt_status_icon_size"
+ android:layout_height="@dimen/media_ttt_status_icon_size"
+ android:layout_marginStart="@dimen/media_ttt_last_item_start_margin"
android:indeterminateTint="?androidprv:attr/colorAccentPrimaryVariant"
style="?android:attr/progressBarStyleSmall"
/>
+ <ImageView
+ android:id="@+id/failure_icon"
+ android:layout_width="@dimen/media_ttt_status_icon_size"
+ android:layout_height="@dimen/media_ttt_status_icon_size"
+ android:layout_marginStart="@dimen/media_ttt_last_item_start_margin"
+ android:src="@drawable/ic_warning"
+ android:tint="@color/GM2_red_500"
+ />
+
<TextView
android:id="@+id/undo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/media_transfer_undo"
android:textColor="?androidprv:attr/textColorOnAccent"
- android:layout_marginStart="12dp"
+ android:layout_marginStart="@dimen/media_ttt_last_item_start_margin"
android:textSize="@dimen/media_ttt_text_size"
android:paddingStart="@dimen/media_ttt_chip_outer_padding"
android:paddingEnd="@dimen/media_ttt_chip_outer_padding"
diff --git a/packages/SystemUI/res/layout/qs_footer_impl.xml b/packages/SystemUI/res/layout/qs_footer_impl.xml
index e70084b80308..5cd9e9485fda 100644
--- a/packages/SystemUI/res/layout/qs_footer_impl.xml
+++ b/packages/SystemUI/res/layout/qs_footer_impl.xml
@@ -43,7 +43,6 @@
android:id="@+id/build"
android:layout_width="0dp"
android:layout_height="match_parent"
- android:paddingStart="@dimen/qs_tile_margin_horizontal"
android:paddingEnd="4dp"
android:layout_weight="1"
android:clickable="true"
@@ -61,10 +60,23 @@
android:layout_gravity="center_vertical"
android:visibility="gone" />
- <View
+ <FrameLayout
android:layout_width="0dp"
android:layout_height="match_parent"
- android:layout_weight="1" />
+ android:layout_weight="1">
+ <com.android.systemui.statusbar.AlphaOptimizedImageView
+ android:id="@android:id/edit"
+ android:layout_width="@dimen/qs_footer_action_button_size"
+ android:layout_height="@dimen/qs_footer_action_button_size"
+ android:layout_gravity="center_vertical|end"
+ android:background="?android:attr/selectableItemBackground"
+ android:clickable="true"
+ android:contentDescription="@string/accessibility_quick_settings_edit"
+ android:focusable="true"
+ android:padding="@dimen/qs_footer_icon_padding"
+ android:src="@*android:drawable/ic_mode_edit"
+ android:tint="?android:attr/textColorPrimary" />
+ </FrameLayout>
</LinearLayout>
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 07f843bb2139..10b8ef414617 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -233,8 +233,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi is nie gekoppel nie"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Helderheid"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Kleuromkering"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Kleurregstelling"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Meer instellings"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Gebruikerinstellings"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Klaar"</string>
@@ -450,8 +449,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Ontsluit om te gebruik"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Kon nie jou kaarte kry nie; probeer later weer"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Sluitskerminstellings"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"Skandeer QR"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"Klik om \'n QR-kode te skandeer"</string>
+ <string name="qr_code_scanner_title" msgid="5660820608548306581">"QR-kode"</string>
+ <string name="qr_code_scanner_description" msgid="7937603775306661863">"Tik om te skandeer"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Werkprofiel"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Vliegtuigmodus"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Jy sal nie jou volgende wekker <xliff:g id="WHEN">%1$s</xliff:g> hoor nie"</string>
@@ -792,7 +791,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Speel <xliff:g id="SONG_NAME">%1$s</xliff:g> deur <xliff:g id="ARTIST_NAME">%2$s</xliff:g> vanaf <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Speel <xliff:g id="SONG_NAME">%1$s</xliff:g> vanaf <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Ontdoen"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Beweeg nader om op <xliff:g id="DEVICENAME">%1$s</xliff:g> te speel"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"Speel tans op <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Onaktief, gaan program na"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Nie gekry nie"</string>
@@ -880,4 +880,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Kies gebruiker"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"Programme wat op die agtergrond werk"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Stop"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-af/tiles_states_strings.xml b/packages/SystemUI/res/values-af/tiles_states_strings.xml
index 4b1a5b85d1d2..08e60c51ece3 100644
--- a/packages/SystemUI/res/values-af/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-af/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"Af"</item>
<item msgid="2075645297847971154">"Aan"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"Onbeskikbaar"</item>
+ <item msgid="1909756493418256167">"Af"</item>
+ <item msgid="4531508423703413340">"Aan"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"Onbeskikbaar"</item>
<item msgid="9103697205127645916">"Af"</item>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index c31179c643d2..6fe6f539df3c 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -233,8 +233,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi አልተገናኘም"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"ብሩህነት"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"ተቃራኒ ቀለም"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"የቀለም ማስተካከያ"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"ተጨማሪ ቅንብሮች"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"የተጠቃሚ ቅንብሮች"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"ተከናውኗል"</string>
@@ -450,8 +449,9 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"ለማየት ይክፈቱ"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"የእርስዎን ካርዶች ማግኘት ላይ ችግር ነበር፣ እባክዎ ቆይተው እንደገና ይሞክሩ"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"የገጽ መቆለፊያ ቅንብሮች"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR ቃኝ"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"QR ኮድን ለመቃኘት ጠቅ ያድርጉ"</string>
+ <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+ <skip />
+ <string name="qr_code_scanner_description" msgid="7937603775306661863">"ለመቃኘት መታ ያድርጉ"</string>
<string name="status_bar_work" msgid="5238641949837091056">"የስራ መገለጫ"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"የአውሮፕላን ሁነታ"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"የእርስዎን ቀጣይ ማንቂያ <xliff:g id="WHEN">%1$s</xliff:g> አይሰሙም"</string>
@@ -792,7 +792,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="SONG_NAME">%1$s</xliff:g> በ<xliff:g id="ARTIST_NAME">%2$s</xliff:g> ከ<xliff:g id="APP_LABEL">%3$s</xliff:g> ያጫውቱ"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> ከ<xliff:g id="APP_LABEL">%2$s</xliff:g> ያጫውቱ"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"ቀልብስ"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"በ<xliff:g id="DEVICENAME">%1$s</xliff:g> ላይ ለማጫወት ጠጋ ያድርጉ"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"በ<xliff:g id="DEVICENAME">%1$s</xliff:g> ላይ በማጫወት ላይ"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"ንቁ ያልኾነ፣ መተግበሪያን ይፈትሹ"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"አልተገኘም"</string>
@@ -880,4 +881,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"ተጠቃሚን ይምረጡ"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"ከበስተጀርባ የሚሠሩ መተግበሪያዎች"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"መቆሚያ"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-am/tiles_states_strings.xml b/packages/SystemUI/res/values-am/tiles_states_strings.xml
index 0f7ee3ecd8d2..c464f9a98cf7 100644
--- a/packages/SystemUI/res/values-am/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-am/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"ጠፍቷል"</item>
<item msgid="2075645297847971154">"በርቷል"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"አይገኝም"</item>
+ <item msgid="1909756493418256167">"አጥፋ"</item>
+ <item msgid="4531508423703413340">"አብራ"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"አይገኝም"</item>
<item msgid="9103697205127645916">"ጠፍቷል"</item>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index ec4816fd448a..5a0c7a1969fc 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -237,8 +237,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"‏لم يتم الاتصال بشبكة Wi-Fi."</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"السطوع"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"قلب الألوان"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"تصحيح الألوان"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"المزيد من الإعدادات"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"إعدادات المستخدم"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"تم"</string>
@@ -462,8 +461,10 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"فتح القفل للاستخدام"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"حدثت مشكلة أثناء الحصول على البطاقات، يُرجى إعادة المحاولة لاحقًا."</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"إعدادات شاشة القفل"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"مسح رمز الاستجابة السريعة ضوئيًا"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"انقر لمسح رمز الاستجابة السريعة ضوئيًا."</string>
+ <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+ <skip />
+ <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+ <skip />
<string name="status_bar_work" msgid="5238641949837091056">"الملف الشخصي للعمل"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"وضع الطيران"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"لن تسمع المنبّه القادم في <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -816,7 +817,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"تشغيل <xliff:g id="SONG_NAME">%1$s</xliff:g> للفنان <xliff:g id="ARTIST_NAME">%2$s</xliff:g> من تطبيق <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"تشغيل <xliff:g id="SONG_NAME">%1$s</xliff:g> من تطبيق <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"تراجع"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"عليك الاقتراب لتشغيل الموسيقى على <xliff:g id="DEVICENAME">%1$s</xliff:g>."</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"جارٍ تشغيل الموسيقى على <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"غير نشط، تحقّق من التطبيق."</string>
<string name="controls_error_removed" msgid="6675638069846014366">"لم يتم العثور عليه."</string>
@@ -904,4 +906,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"اختيار المستخدم"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"التطبيقات التي يتم تشغيلها في الخلفية"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"إيقاف"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ar/tiles_states_strings.xml b/packages/SystemUI/res/values-ar/tiles_states_strings.xml
index 2da87c467177..2bfcf7c57b8b 100644
--- a/packages/SystemUI/res/values-ar/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ar/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"الميزة غير مفعّلة"</item>
<item msgid="2075645297847971154">"الميزة مفعّلة"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"غير متوفّر"</item>
+ <item msgid="1909756493418256167">"غير مفعّل"</item>
+ <item msgid="4531508423703413340">"مفعّل"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"الميزة غير متاحة"</item>
<item msgid="9103697205127645916">"الميزة غير مفعّلة"</item>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index f118633b199b..bf35ad202870 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -233,8 +233,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"ৱাই-ফাইৰ সৈতে সংযোগ হৈ থকা নাই"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"উজ্জ্বলতা"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"ৰং বিপৰীতকৰণ"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"ৰং শুধৰণী"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"অধিক ছেটিং"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"ব্যৱহাৰকাৰীৰ ছেটিং"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"সম্পন্ন কৰা হ’ল"</string>
@@ -450,8 +449,10 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"ব্যৱহাৰ কৰিবলৈ আনলক কৰক"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"আপোনাৰ কাৰ্ড লাভ কৰোঁতে এটা সমস্যা হৈছে, অনুগ্ৰহ কৰি পাছত পুনৰ চেষ্টা কৰক"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"লক স্ক্ৰীনৰ ছেটিং"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"কিউআৰ স্কেন কৰক"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"এটা কিউআৰ ক’ড স্কেন কৰিবলৈ ক্লিক কৰক"</string>
+ <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+ <skip />
+ <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+ <skip />
<string name="status_bar_work" msgid="5238641949837091056">"কৰ্মস্থানৰ প্ৰ\'ফাইল"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"এয়াৰপ্লেইন ম\'ড"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"আপুনি আপোনাৰ পিছৰটো এলাৰ্ম <xliff:g id="WHEN">%1$s</xliff:g> বজাত শুনা নাপাব"</string>
@@ -792,7 +793,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="APP_LABEL">%3$s</xliff:g>ত <xliff:g id="ARTIST_NAME">%2$s</xliff:g>ৰ <xliff:g id="SONG_NAME">%1$s</xliff:g> গীতটো প্লে’ কৰক"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="APP_LABEL">%2$s</xliff:g>ত <xliff:g id="SONG_NAME">%1$s</xliff:g> গীতটো প্লে’ কৰক"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"আনডু কৰক"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"<xliff:g id="DEVICENAME">%1$s</xliff:g>ত প্লে\' কৰিবলৈ ওপৰলৈ যাওক"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"<xliff:g id="DEVICENAME">%1$s</xliff:g>ত প্লে\' কৰি থকা হৈছে"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"সক্ৰিয় নহয়, এপ্‌টো পৰীক্ষা কৰক"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"বিচাৰি পোৱা নগ’ল"</string>
@@ -880,4 +882,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"ব্যৱহাৰকাৰী বাছনি কৰক"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"নেপথ্যত চলি থকা এপ্"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"বন্ধ কৰক"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-as/tiles_states_strings.xml b/packages/SystemUI/res/values-as/tiles_states_strings.xml
index 2ede37473ea5..ba66f8c5f1cd 100644
--- a/packages/SystemUI/res/values-as/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-as/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"অফ আছে"</item>
<item msgid="2075645297847971154">"অন কৰা আছে"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"উপলব্ধ নহয়"</item>
+ <item msgid="1909756493418256167">"অফ আছে"</item>
+ <item msgid="4531508423703413340">"অন কৰা আছে"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"উপলব্ধ নহয়"</item>
<item msgid="9103697205127645916">"অফ আছে"</item>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index 9abe0a7a04f7..d4917248de33 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -233,8 +233,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi qoşulu deyil"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Parlaqlıq"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Rəng inversiyası"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Rəng korreksiyası"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Digər ayarlar"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"İstifadəçi ayarları"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Hazır"</string>
@@ -450,8 +449,9 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"İstifadə etmək üçün kiliddən çıxarın"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Kartların əldə edilməsində problem oldu, sonra yenidən cəhd edin"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Kilid ekranı ayarları"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR kodunu skan edin"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"QR kodu skan etmək üçün tıklayın"</string>
+ <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+ <skip />
+ <string name="qr_code_scanner_description" msgid="7937603775306661863">"Skanlamaq üçün toxunun"</string>
<string name="status_bar_work" msgid="5238641949837091056">"İş profili"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Təyyarə rejimi"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"<xliff:g id="WHEN">%1$s</xliff:g> zaman növbəti xəbərdarlığınızı eşitməyəcəksiniz"</string>
@@ -792,7 +792,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="ARTIST_NAME">%2$s</xliff:g> tərəfindən <xliff:g id="SONG_NAME">%1$s</xliff:g> mahnısını <xliff:g id="APP_LABEL">%3$s</xliff:g> tətbiqindən oxudun"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> mahnısını <xliff:g id="APP_LABEL">%2$s</xliff:g> tətbiqindən oxudun"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Geri qaytarın"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"<xliff:g id="DEVICENAME">%1$s</xliff:g> cihazında oxutmaq üçün yaxınlaşın"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"<xliff:g id="DEVICENAME">%1$s</xliff:g> cihazında oxudulur"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Aktiv deyil, tətbiqi yoxlayın"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Tapılmadı"</string>
@@ -880,4 +881,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"İstifadəçi seçin"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"Arxa fonda işləyən tətbiqlər"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Dayandırın"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-az/tiles_states_strings.xml b/packages/SystemUI/res/values-az/tiles_states_strings.xml
index f52a9e1bf6fa..368966038b3d 100644
--- a/packages/SystemUI/res/values-az/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-az/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"Deaktiv"</item>
<item msgid="2075645297847971154">"Aktiv"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"Əlçatmazdır"</item>
+ <item msgid="1909756493418256167">"Deaktiv"</item>
+ <item msgid="4531508423703413340">"Aktiv"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"Əlçatan deyil"</item>
<item msgid="9103697205127645916">"Deaktiv"</item>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index 02c4d920bf83..955096e35628 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -234,8 +234,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"WiFi nije povezan"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Osvetljenost"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inverzija boja"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Korekcija boja"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Još podešavanja"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Korisnička podešavanja"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Gotovo"</string>
@@ -453,8 +452,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Otključaj radi korišćenja"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Došlo je do problema pri preuzimanju kartica. Probajte ponovo kasnije"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Podešavanja zaključanog ekrana"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"Skeniraj QR kôd"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"Kliknite da biste skenirali QR kôd"</string>
+ <string name="qr_code_scanner_title" msgid="5660820608548306581">"QR kôd"</string>
+ <string name="qr_code_scanner_description" msgid="7937603775306661863">"Dodirnite da biste skenirali"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Poslovni profil"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Režim rada u avionu"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Nećete čuti sledeći alarm u <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -798,7 +797,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Pustite <xliff:g id="SONG_NAME">%1$s</xliff:g> izvođača <xliff:g id="ARTIST_NAME">%2$s</xliff:g> iz aplikacije <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Pustite <xliff:g id="SONG_NAME">%1$s</xliff:g> iz aplikacije <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Opozovi"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Približite da biste puštali muziku na: <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"Pušta se na: <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Neaktivno. Vidite aplikaciju"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Nije pronađeno"</string>
@@ -886,4 +886,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Izaberite korisnika"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"Aplikacije pokrenute u pozadini"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Zaustavi"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/tiles_states_strings.xml b/packages/SystemUI/res/values-b+sr+Latn/tiles_states_strings.xml
index d9e0bfca36ff..6e2b7d1bbe57 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"Isključeno"</item>
<item msgid="2075645297847971154">"Uključeno"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"Nedostupno"</item>
+ <item msgid="1909756493418256167">"Isključeno"</item>
+ <item msgid="4531508423703413340">"Uključeno"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"Nedostupno"</item>
<item msgid="9103697205127645916">"Isključeno"</item>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index 88f58ac105d3..4d11111832b1 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -235,8 +235,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Няма падключэння да Wi-Fi"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Яркасць"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Інверсія колераў"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Карэкцыя колераў"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Дадатковыя налады"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Налады карыстальніка"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Гатова"</string>
@@ -354,7 +353,7 @@
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Паказ апавяшчэнняў прыпынены ў рэжыме \"Не турбаваць\""</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Пачаць зараз"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Апавяшчэнняў няма"</string>
- <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Гэта прылада знаходзіцца пад кантролем вашых бацькоў"</string>
+ <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Гэта прылада знаходзіцца пад кантролем бацькоў"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Ваша арганізацыя валодае гэтай прыладай і можа кантраляваць сеткавы трафік"</string>
<string name="quick_settings_disclosure_named_management_monitoring" msgid="2831423806103479812">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> валодае гэтай прыладай і можа кантраляваць сеткавы трафік"</string>
<string name="quick_settings_financed_disclosure_named_management" msgid="2307703784594859524">"Гэта прылада належыць арганізацыі \"<xliff:g id="ORGANIZATION_NAME">%s</xliff:g>\""</string>
@@ -393,7 +392,7 @@
<string name="monitoring_description_personal_profile_named_vpn" msgid="8179722332380953673">"Ваш асабісты профіль падключаны да праграмы <xliff:g id="VPN_APP">%1$s</xliff:g>, якая можа сачыць за вашай сеткавай актыўнасцю, уключаючы электронную пошту, праграмы і вэб-сайты."</string>
<string name="monitoring_description_vpn_settings_separator" msgid="8292589617720435430">" ,"</string>
<string name="monitoring_description_vpn_settings" msgid="5264167033247632071">"Адкрыйце налады VPN"</string>
- <string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Гэта прылада знаходзіцца пад кантролем вашых бацькоў. Бацькі могуць праглядаць і кантраляваць вашу інфармацыю, напрыклад пра праграмы, якія вы выкарыстоўваеце, даныя пра ваша месцазнаходжанне і час карыстання прыладай."</string>
+ <string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Гэта прылада знаходзіцца пад кантролем бацькоў. Бацькі могуць праглядаць і кантраляваць тваю інфармацыю, напрыклад пра праграмы, якія ты выкарыстоўваеш, даныя пра тваё месцазнаходжанне і час карыстання прыладай."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Разблакіравана з дапамогай TrustAgent"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
@@ -456,8 +455,10 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Разблакіраваць для выкарыстання"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Узнікла праблема з загрузкай вашых карт. Паўтарыце спробу пазней"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Налады экрана блакіроўкі"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"Адсканіраваць QR-код"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"Націсніце, каб адсканіраваць QR-код"</string>
+ <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+ <skip />
+ <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+ <skip />
<string name="status_bar_work" msgid="5238641949837091056">"Працоўны профіль"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Рэжым палёту"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Вы не пачуеце наступны будзільнік <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -804,7 +805,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Прайграйце кампазіцыю \"<xliff:g id="SONG_NAME">%1$s</xliff:g>\" (выканаўца – <xliff:g id="ARTIST_NAME">%2$s</xliff:g>) з дапамогай праграмы \"<xliff:g id="APP_LABEL">%3$s</xliff:g>\""</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Прайграйце кампазіцыю \"<xliff:g id="SONG_NAME">%1$s</xliff:g>\" з дапамогай праграмы \"<xliff:g id="APP_LABEL">%2$s</xliff:g>\""</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Адрабіць"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Наблізьцеся, каб прайграць музыку на прыладзе \"<xliff:g id="DEVICENAME">%1$s</xliff:g>\""</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"Прайграецца на прыладзе \"<xliff:g id="DEVICENAME">%1$s</xliff:g>\""</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Неактыўна, праверце праграму"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Не знойдзена"</string>
@@ -892,4 +894,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Выбар карыстальніка"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"Праграмы працуюць у фонавым рэжыме"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Спыніць"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-be/tiles_states_strings.xml b/packages/SystemUI/res/values-be/tiles_states_strings.xml
index 5c7c40fed7a7..aef519fe9dcb 100644
--- a/packages/SystemUI/res/values-be/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-be/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"Выключана"</item>
<item msgid="2075645297847971154">"Уключана"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"Недаступна"</item>
+ <item msgid="1909756493418256167">"Выключана"</item>
+ <item msgid="4531508423703413340">"Уключана"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"Недаступна"</item>
<item msgid="9103697205127645916">"Выключана"</item>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 8b455daef832..aaa1f3abab11 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -233,8 +233,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"не е установена връзка с Wi-Fi"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Яркост"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Инвертиране на цветовете"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Корекция на цветове"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Още настройки"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Потребителски настройки"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Готово"</string>
@@ -450,8 +449,10 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Отключване с цел използване"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"При извличането на картите ви възникна проблем. Моля, опитайте отново по-късно"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Настройки за заключения екран"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"Сканиране на QR код"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"Кликнете, за да сканирате QR код"</string>
+ <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+ <skip />
+ <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+ <skip />
<string name="status_bar_work" msgid="5238641949837091056">"Потребителски профил в Work"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Самолетен режим"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Няма да чуете следващия си будилник в <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -792,7 +793,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Пускане на <xliff:g id="SONG_NAME">%1$s</xliff:g> на <xliff:g id="ARTIST_NAME">%2$s</xliff:g> от <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Пускане на <xliff:g id="SONG_NAME">%1$s</xliff:g> от <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Отмяна"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Преместете се по-близо, за да се възпроизведе на <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"Възпроизвежда се на <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Неактивно, проверете прилож."</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Не е намерено"</string>
@@ -880,4 +882,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Избор на потребител"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"Приложения, които се изпълняват на заден план"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Спиране"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-bg/tiles_states_strings.xml b/packages/SystemUI/res/values-bg/tiles_states_strings.xml
index 6839b830283c..0900c521abf1 100644
--- a/packages/SystemUI/res/values-bg/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-bg/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"Изкл."</item>
<item msgid="2075645297847971154">"Вкл."</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"Не е налице"</item>
+ <item msgid="1909756493418256167">"Изкл."</item>
+ <item msgid="4531508423703413340">"Вкл."</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"Не е налице"</item>
<item msgid="9103697205127645916">"Изкл."</item>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index 958853ec91e4..be290afede2f 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -233,8 +233,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"ওয়াই-ফাই কানেক্ট করা নেই"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"উজ্জ্বলতা"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"কালার ইনভার্সন"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"রঙ সংশোধন"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"আরও সেটিংস"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"ব্যবহারকারী সেটিংস"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"সম্পন্ন হয়েছে"</string>
@@ -450,8 +449,9 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"ব্যবহার করতে আনলক করুন"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"আপনার কার্ড সংক্রান্ত তথ্য পেতে সমস্যা হয়েছে, পরে আবার চেষ্টা করুন"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"লক স্ক্রিন সেটিংস"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR কোড স্ক্যান করুন"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"QR কোড স্ক্যান করতে ক্লিক করুন"</string>
+ <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+ <skip />
+ <string name="qr_code_scanner_description" msgid="7937603775306661863">"স্ক্যান করতে ট্যাপ করুন"</string>
<string name="status_bar_work" msgid="5238641949837091056">"কাজের প্রোফাইল"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"বিমান মোড"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"আপনি আপনার পরবর্তী <xliff:g id="WHEN">%1$s</xliff:g> অ্যালার্ম শুনতে পাবেন না"</string>
@@ -792,7 +792,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="ARTIST_NAME">%2$s</xliff:g>-এর <xliff:g id="SONG_NAME">%1$s</xliff:g> গানটি <xliff:g id="APP_LABEL">%3$s</xliff:g> অ্যাপে চালান"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> গানটি <xliff:g id="APP_LABEL">%2$s</xliff:g> অ্যাপে চালান"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"আগের অবস্থায় ফিরুন"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"<xliff:g id="DEVICENAME">%1$s</xliff:g>-এ চালাতে আরও কাছে আনুন"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"<xliff:g id="DEVICENAME">%1$s</xliff:g>-এ ভিডিও চালানো হচ্ছে"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"বন্ধ আছে, অ্যাপ চেক করুন"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"খুঁজে পাওয়া যায়নি"</string>
@@ -880,4 +881,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"ব্যবহারকারী বেছে নিন"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"ব্যাকগ্রাউন্ডে অ্যাপ চলছে"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"বন্ধ করুন"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-bn/tiles_states_strings.xml b/packages/SystemUI/res/values-bn/tiles_states_strings.xml
index 7a6b3ac1c371..5358e5d2ec43 100644
--- a/packages/SystemUI/res/values-bn/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-bn/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"বন্ধ আছে"</item>
<item msgid="2075645297847971154">"চালু আছে"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"অনুপলভ্য"</item>
+ <item msgid="1909756493418256167">"বন্ধ আছে"</item>
+ <item msgid="4531508423703413340">"চালু আছে"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"উপলভ্য নেই"</item>
<item msgid="9103697205127645916">"বন্ধ আছে"</item>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index 092d3df05c7a..eb45708f1caa 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -234,8 +234,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"WiFi mreža nije povezana"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Osvjetljenje"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inverzija boja"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Ispravka boje"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Više postavki"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Korisničke postavke"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Gotovo"</string>
@@ -453,8 +452,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Otključajte da koristite"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Došlo je do problema prilikom preuzimanja vaših kartica. Pokušajte ponovo kasnije"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Postavke zaključavanja ekrana"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"Skeniraj QR"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"Kliknite da skenirate QR kôd"</string>
+ <string name="qr_code_scanner_title" msgid="5660820608548306581">"QR kôd"</string>
+ <string name="qr_code_scanner_description" msgid="7937603775306661863">"Dodirnite da skenirate"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Profil za posao"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Način rada u avionu"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Nećete čuti sljedeći alarm u <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -798,7 +797,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Reproducirajte pjesmu <xliff:g id="SONG_NAME">%1$s</xliff:g> izvođača <xliff:g id="ARTIST_NAME">%2$s</xliff:g> pomoću aplikacije <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Reproducirajte pjesmu <xliff:g id="SONG_NAME">%1$s</xliff:g> pomoću aplikacije <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Poništi"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Približite se da reproducirate na uređaju <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"Reproducira se na uređaju <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Neaktivno, vidite aplikaciju"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Nije pronađeno"</string>
@@ -886,4 +886,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Odaberite korisnika"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"Aplikacije su aktivne u pozadini"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Zaustavi"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-bs/tiles_states_strings.xml b/packages/SystemUI/res/values-bs/tiles_states_strings.xml
index d9e0bfca36ff..6e2b7d1bbe57 100644
--- a/packages/SystemUI/res/values-bs/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-bs/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"Isključeno"</item>
<item msgid="2075645297847971154">"Uključeno"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"Nedostupno"</item>
+ <item msgid="1909756493418256167">"Isključeno"</item>
+ <item msgid="4531508423703413340">"Uključeno"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"Nedostupno"</item>
<item msgid="9103697205127645916">"Isključeno"</item>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 6ae7d1617df6..06799a21da4c 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -233,8 +233,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"La Wi‑Fi no està connectada"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Brillantor"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inversió de colors"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Correcció de color"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Més opcions"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Configuració d\'usuari"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Fet"</string>
@@ -450,8 +449,10 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Desbloqueja per utilitzar"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Hi ha hagut un problema en obtenir les teves targetes; torna-ho a provar més tard"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Configuració de la pantalla de bloqueig"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"Escaneja un codi QR"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"Fes clic per escanejar un codi QR"</string>
+ <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+ <skip />
+ <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+ <skip />
<string name="status_bar_work" msgid="5238641949837091056">"Perfil de treball"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Mode d\'avió"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"<xliff:g id="WHEN">%1$s</xliff:g> no sentiràs la pròxima alarma"</string>
@@ -792,7 +793,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Reprodueix <xliff:g id="SONG_NAME">%1$s</xliff:g> (<xliff:g id="ARTIST_NAME">%2$s</xliff:g>) des de l\'aplicació <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Reprodueix <xliff:g id="SONG_NAME">%1$s</xliff:g> des de l\'aplicació <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Desfés"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Mou més a prop per reproduir a <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"S\'està reproduint a <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Inactiu; comprova l\'aplicació"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"No s\'ha trobat"</string>
@@ -880,4 +882,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Selecciona un usuari"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"Aplicacions que s\'executen en segon pla"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Atura"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ca/tiles_states_strings.xml b/packages/SystemUI/res/values-ca/tiles_states_strings.xml
index 28f3da4650eb..2738ecfcbd88 100644
--- a/packages/SystemUI/res/values-ca/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ca/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"Desactivat"</item>
<item msgid="2075645297847971154">"Activat"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"No disponible"</item>
+ <item msgid="1909756493418256167">"Desactivada"</item>
+ <item msgid="4531508423703413340">"Activada"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"No disponible"</item>
<item msgid="9103697205127645916">"Desactivat"</item>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 7e716160939d..4b63cd807084 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -235,8 +235,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Není připojena Wi-Fi"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Jas"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Převrácení barev"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Korekce barev"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Další nastavení"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Uživatelské nastavení"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Hotovo"</string>
@@ -456,8 +455,9 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Odemknout a použít"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Při načítání karet došlo k problému, zkuste to později"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Nastavení obrazovky uzamčení"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"Naskenovat QR kód"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"Kliknutím naskenujete QR kód"</string>
+ <string name="qr_code_scanner_title" msgid="5660820608548306581">"QR kód"</string>
+ <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+ <skip />
<string name="status_bar_work" msgid="5238641949837091056">"Pracovní profil"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Režim Letadlo"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Svůj další budík <xliff:g id="WHEN">%1$s</xliff:g> neuslyšíte"</string>
@@ -804,7 +804,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Přehrát skladbu <xliff:g id="SONG_NAME">%1$s</xliff:g> od interpreta <xliff:g id="ARTIST_NAME">%2$s</xliff:g> z aplikace <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Přehrát skladbu <xliff:g id="SONG_NAME">%1$s</xliff:g> z aplikace <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Vrátit zpět"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Pokud chcete přehrávat na zařízení <xliff:g id="DEVICENAME">%1$s</xliff:g>, přibližte se k němu"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"Přehrává se na zařízení <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Neaktivní, zkontrolujte aplikaci"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Nenalezeno"</string>
@@ -892,4 +893,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Zvolte uživatele"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"Aplikace běžící na pozadí"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Konec"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-cs/tiles_states_strings.xml b/packages/SystemUI/res/values-cs/tiles_states_strings.xml
index ce64e273dc02..cd667cbdc2f0 100644
--- a/packages/SystemUI/res/values-cs/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-cs/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"Vyp"</item>
<item msgid="2075645297847971154">"Zap"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"Nedostupné"</item>
+ <item msgid="1909756493418256167">"Vyp"</item>
+ <item msgid="4531508423703413340">"Zap"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"Nedostupné"</item>
<item msgid="9103697205127645916">"Vyp"</item>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 919f57c307ff..ad92cad640c9 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -233,8 +233,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Manglende Wi-Fi-forbindelse"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Lysstyrke"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Ombytning af farver"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Farvekorrigering"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Flere indstillinger"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Brugerindstillinger"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Udfør"</string>
@@ -450,8 +449,10 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Lås op for at bruge"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Dine kort kunne ikke hentes. Prøv igen senere."</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Lås skærmindstillinger"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"Scan QR-kode"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"Klik for at scanne en QR-kode"</string>
+ <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+ <skip />
+ <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+ <skip />
<string name="status_bar_work" msgid="5238641949837091056">"Arbejdsprofil"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Flytilstand"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Du vil ikke kunne høre din næste alarm <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -792,7 +793,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Afspil <xliff:g id="SONG_NAME">%1$s</xliff:g> af <xliff:g id="ARTIST_NAME">%2$s</xliff:g> via <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Afspil <xliff:g id="SONG_NAME">%1$s</xliff:g> via <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Fortryd"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Flyt enheden tættere på for at afspille på <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"Afspilles på <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Inaktiv. Tjek appen"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Ikke fundet"</string>
@@ -880,4 +882,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Vælg bruger"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"Apps, der kører i baggrunden"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Stop"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-da/tiles_states_strings.xml b/packages/SystemUI/res/values-da/tiles_states_strings.xml
index 9e4c6f4b8459..5ec01fe39c4f 100644
--- a/packages/SystemUI/res/values-da/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-da/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"Fra"</item>
<item msgid="2075645297847971154">"Til"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"Ikke tilgængelig"</item>
+ <item msgid="1909756493418256167">"Fra"</item>
+ <item msgid="4531508423703413340">"Til"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"Ikke tilgængelig"</item>
<item msgid="9103697205127645916">"Fra"</item>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index ce58c3d080c8..8b514829614f 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -233,8 +233,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"WLAN nicht verbunden"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Helligkeit"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Farbumkehr"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Farbkorrektur"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Weitere Einstellungen"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Nutzereinstellungen"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Fertig"</string>
@@ -450,8 +449,9 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Zum Verwenden entsperren"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Beim Abrufen deiner Karten ist ein Fehler aufgetreten – bitte versuch es später noch einmal"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Einstellungen für den Sperrbildschirm"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR-Code scannen"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"Klicken, um einen QR-Code zu scannen"</string>
+ <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+ <skip />
+ <string name="qr_code_scanner_description" msgid="7937603775306661863">"Zum Scannen tippen"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Arbeitsprofil"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Flugmodus"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Lautloser Weckruf <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -792,7 +792,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="SONG_NAME">%1$s</xliff:g> von <xliff:g id="ARTIST_NAME">%2$s</xliff:g> über <xliff:g id="APP_LABEL">%3$s</xliff:g> wiedergeben"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> über <xliff:g id="APP_LABEL">%2$s</xliff:g> wiedergeben"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Rückgängig machen"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Gehe für die Wiedergabe näher an <xliff:g id="DEVICENAME">%1$s</xliff:g> heran"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"Wird auf <xliff:g id="DEVICENAME">%1$s</xliff:g> abgespielt"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Inaktiv – sieh in der App nach"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Nicht gefunden"</string>
@@ -880,4 +881,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Nutzer auswählen"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"Apps, die im Hintergrund ausgeführt werden"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Beenden"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-de/tiles_states_strings.xml b/packages/SystemUI/res/values-de/tiles_states_strings.xml
index e958670b502e..72476456b248 100644
--- a/packages/SystemUI/res/values-de/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-de/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"Aus"</item>
<item msgid="2075645297847971154">"An"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"Nicht verfügbar"</item>
+ <item msgid="1909756493418256167">"Aus"</item>
+ <item msgid="4531508423703413340">"An"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"Nicht verfügbar"</item>
<item msgid="9103697205127645916">"Aus"</item>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 96aed4c7264c..9e61f1a29135 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -233,8 +233,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Το Wi-Fi δεν είναι συνδεδεμένο"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Φωτεινότητα"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Αντιστροφή χρωμάτων"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Διόρθωση χρωμάτων"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Περισσότερες ρυθμίσεις"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Ρυθμίσεις χρήστη"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Τέλος"</string>
@@ -450,8 +449,9 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Ξεκλείδωμα για χρήση"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Παρουσιάστηκε πρόβλημα με τη λήψη των καρτών σας. Δοκιμάστε ξανά αργότερα"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Ρυθμίσεις κλειδώματος οθόνης"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"Σάρωση κωδικού QR"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"Κάντε κλικ για σάρωση κωδικού QR"</string>
+ <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+ <skip />
+ <string name="qr_code_scanner_description" msgid="7937603775306661863">"Πατήστε για σάρωση"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Προφίλ εργασίας"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Λειτουργία πτήσης"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Δεν θα ακούσετε το επόμενο ξυπνητήρι σας <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -792,7 +792,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Αναπαραγωγή του <xliff:g id="SONG_NAME">%1$s</xliff:g> από <xliff:g id="ARTIST_NAME">%2$s</xliff:g> στην εφαρμογή <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Αναπαραγωγή του <xliff:g id="SONG_NAME">%1$s</xliff:g> στην εφαρμογή <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Αναίρεση"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Πλησιάστε για αναπαραγωγή στη συσκευή <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"Αναπαραγωγή στη συσκευή <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Ανενεργό, έλεγχος εφαρμογής"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Δεν βρέθηκε."</string>
@@ -880,4 +881,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Επιλογή χρήστη"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"Οι εφαρμογές βρίσκονται σε εξέλιξη στο παρασκήνιο"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Διακοπή"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-el/tiles_states_strings.xml b/packages/SystemUI/res/values-el/tiles_states_strings.xml
index 1c9583518595..4dca192a877c 100644
--- a/packages/SystemUI/res/values-el/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-el/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"Ανενεργό"</item>
<item msgid="2075645297847971154">"Ενεργό"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"Μη διαθέσιμη"</item>
+ <item msgid="1909756493418256167">"Ανενεργή"</item>
+ <item msgid="4531508423703413340">"Ενεργή"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"Μη διαθέσιμο"</item>
<item msgid="9103697205127645916">"Ανενεργό"</item>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index 8504cb77cbb3..79c9ca2de1db 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -449,8 +449,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Unlock to use"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"There was a problem getting your cards. Please try again later."</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Lock screen settings"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"Scan QR"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"Click to scan a QR code"</string>
+ <string name="qr_code_scanner_title" msgid="5660820608548306581">"QR code"</string>
+ <string name="qr_code_scanner_description" msgid="7937603775306661863">"Tap to scan"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Work profile"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Aeroplane mode"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"You won\'t hear your next alarm <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -791,7 +791,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Play <xliff:g id="SONG_NAME">%1$s</xliff:g> by <xliff:g id="ARTIST_NAME">%2$s</xliff:g> from <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Play <xliff:g id="SONG_NAME">%1$s</xliff:g> from <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Undo"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Move closer to play on <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"Playing on <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Inactive, check app"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Not found"</string>
@@ -879,4 +880,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Select user"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"Apps running in the background"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Stop"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index 68c5e649c56d..605811d571ee 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -449,8 +449,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Unlock to use"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"There was a problem getting your cards. Please try again later."</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Lock screen settings"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"Scan QR"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"Click to scan a QR code"</string>
+ <string name="qr_code_scanner_title" msgid="5660820608548306581">"QR code"</string>
+ <string name="qr_code_scanner_description" msgid="7937603775306661863">"Tap to scan"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Work profile"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Airplane mode"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"You won\'t hear your next alarm <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -791,7 +791,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Play <xliff:g id="SONG_NAME">%1$s</xliff:g> by <xliff:g id="ARTIST_NAME">%2$s</xliff:g> from <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Play <xliff:g id="SONG_NAME">%1$s</xliff:g> from <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Undo"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Move closer to play on <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"Playing on <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Inactive, check app"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Not found"</string>
@@ -879,4 +880,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Select user"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"Apps running in the background"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Stop"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 8504cb77cbb3..79c9ca2de1db 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -449,8 +449,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Unlock to use"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"There was a problem getting your cards. Please try again later."</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Lock screen settings"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"Scan QR"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"Click to scan a QR code"</string>
+ <string name="qr_code_scanner_title" msgid="5660820608548306581">"QR code"</string>
+ <string name="qr_code_scanner_description" msgid="7937603775306661863">"Tap to scan"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Work profile"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Aeroplane mode"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"You won\'t hear your next alarm <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -791,7 +791,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Play <xliff:g id="SONG_NAME">%1$s</xliff:g> by <xliff:g id="ARTIST_NAME">%2$s</xliff:g> from <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Play <xliff:g id="SONG_NAME">%1$s</xliff:g> from <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Undo"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Move closer to play on <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"Playing on <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Inactive, check app"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Not found"</string>
@@ -879,4 +880,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Select user"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"Apps running in the background"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Stop"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index 8504cb77cbb3..79c9ca2de1db 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -449,8 +449,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Unlock to use"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"There was a problem getting your cards. Please try again later."</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Lock screen settings"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"Scan QR"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"Click to scan a QR code"</string>
+ <string name="qr_code_scanner_title" msgid="5660820608548306581">"QR code"</string>
+ <string name="qr_code_scanner_description" msgid="7937603775306661863">"Tap to scan"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Work profile"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Aeroplane mode"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"You won\'t hear your next alarm <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -791,7 +791,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Play <xliff:g id="SONG_NAME">%1$s</xliff:g> by <xliff:g id="ARTIST_NAME">%2$s</xliff:g> from <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Play <xliff:g id="SONG_NAME">%1$s</xliff:g> from <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Undo"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Move closer to play on <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"Playing on <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Inactive, check app"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Not found"</string>
@@ -879,4 +880,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Select user"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"Apps running in the background"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Stop"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index 60f725e6450f..389554855332 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -449,8 +449,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‏‏‏‏‎‏‎‏‎‎‎‏‎‏‏‎‏‏‎‎‎‏‎‏‎‏‎‏‏‎‎‎‎‎‎‏‏‎‎‏‏‏‎‏‎‏‏‏‏‎‏‎‎‎‏‎‏‎‎Unlock to use‎‏‎‎‏‎"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‏‏‎‎‏‎‎‏‏‏‎‎‎‏‏‎‎‏‏‏‏‎‎‎‎‏‏‏‏‎‎‏‎‏‏‎‎‎‎‎‎‎‎‎‏‎‎‎‏‏‎‎‏‏‎‏‏‎There was a problem getting your cards, please try again later‎‏‎‎‏‎"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‏‎‎‎‏‏‏‎‏‎‏‏‎‏‏‎‏‏‎‎‎‎‏‎‏‎‎‏‎‏‎‎‏‎‎‎‏‎‏‎‎‏‏‎‎‎‏‏‏‏‏‎‏‎‎‏‎‎Lock screen settings‎‏‎‎‏‎"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‏‎‎‎‏‏‎‎‎‎‎‏‏‏‏‎‏‎‎‏‏‎‏‎‎‎‏‏‎‎‏‏‎‏‎‏‎‎‎‎‎‎‎‎‏‏‏‏‎‎‎‎‎‏‎‎‏‎‎Scan QR‎‏‎‎‏‎"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‏‎‏‏‎‏‎‏‏‎‎‏‎‏‎‏‏‏‏‎‏‏‏‏‎‏‎‏‏‎‏‎‏‎‏‎‎‎‏‏‎‏‎‏‎‏‏‏‎‎‏‏‎‏‎‎‏‎Click to scan a QR code‎‏‎‎‏‎"</string>
+ <string name="qr_code_scanner_title" msgid="5660820608548306581">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‎‏‎‎‎‏‏‏‏‎‏‎‎‎‏‏‎‎‏‎‏‏‎‎‏‏‏‏‎‎‏‎‏‏‎‏‏‏‎‏‏‎‏‏‏‏‏‏‎‏‎‎‏‎‏‎‏‎QR code‎‏‎‎‏‎"</string>
+ <string name="qr_code_scanner_description" msgid="7937603775306661863">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‎‎‎‏‎‏‎‎‎‎‎‎‎‏‎‎‎‏‎‎‏‎‏‎‎‎‎‎‏‎‎‏‎‏‏‏‏‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‎‏‏‏‎Tap to scan‎‏‎‎‏‎"</string>
<string name="status_bar_work" msgid="5238641949837091056">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‎‏‎‏‏‎‎‏‏‎‏‏‎‎‏‎‏‎‎‎‏‏‎‎‏‎‎‎‎‏‏‎‏‎‏‏‏‏‏‏‏‏‏‏‎‎‎‎‎‏‏‏‏‎‎‎‎‎Work profile‎‏‎‎‏‎"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‏‎‏‎‎‏‎‏‎‎‎‎‎‏‏‎‏‎‎‏‏‏‏‎‏‏‏‏‏‏‎‏‏‏‎‏‎‏‏‏‏‏‏‏‏‏‏‎‎‎‏‎‏‎‎‎‏‎Airplane mode‎‏‎‎‏‎"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‎‎‏‏‎‏‏‏‎‎‏‎‎‏‎‎‎‎‎‎‏‏‏‏‎‎‎‎‎‎‏‎‎‎‎‏‏‏‎‏‎‎‎‏‏‏‎‏‏‏‏‏‎‏‏‏‏‏‎You won\'t hear your next alarm ‎‏‎‎‏‏‎<xliff:g id="WHEN">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
@@ -791,7 +791,7 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‎‎‏‏‎‎‎‎‏‏‏‎‏‏‎‏‏‎‎‎‏‏‎‎‎‏‎‏‏‎‏‏‎‏‏‎‎‎‏‎‏‏‎‎‏‎‎‎‏‎‏‏‎‎‎‏‏‎Play ‎‏‎‎‏‏‎<xliff:g id="SONG_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ by ‎‏‎‎‏‏‎<xliff:g id="ARTIST_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎ from ‎‏‎‎‏‏‎<xliff:g id="APP_LABEL">%3$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‎‎‏‏‎‎‏‎‎‏‎‏‏‏‎‎‏‏‏‎‎‎‏‎‏‏‏‎‏‎‏‏‏‎‏‎‎‏‎‎‎‏‎‎‏‎‎‏‏‏‏‏‏‎‎‏‎‏‎Play ‎‏‎‎‏‏‎<xliff:g id="SONG_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ from ‎‏‎‎‏‏‎<xliff:g id="APP_LABEL">%2$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‏‎‎‏‎‎‏‏‏‎‏‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‎‏‎‏‏‎‎‎‎‎‎‏‏‎‎‎‎‏‎‎‏‏‏‏‏‎‏‎‎‎‎‎‎Undo‎‏‎‎‏‎"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‏‎‏‎‎‏‏‎‎‏‎‏‎‏‎‏‎‏‏‎‎‏‏‎‎‏‎‎‎‏‏‏‏‏‎‏‎‏‏‎‏‏‎‏‏‎‎‎‏‏‎‏‏‏‎‏‎‏‎Move closer to play on ‎‏‎‎‏‏‎<xliff:g id="DEVICENAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
+ <string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‏‎‎‎‏‏‎‎‎‏‏‎‎‎‏‏‎‏‎‎‏‎‎‎‏‏‎‎‏‎‏‏‏‏‏‏‎‎‎‎‎‎‏‏‎‎‏‏‏‏‎‏‏‏‎‎‎‎Move closer to play on ‎‏‎‎‏‏‎<xliff:g id="DEVICENAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
<string name="media_transfer_playing" msgid="3760048096352107789">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‎‎‎‏‎‏‏‏‎‎‏‎‏‏‏‏‏‏‏‎‏‎‎‏‎‎‏‏‏‎‎‎‏‎‏‏‏‎‎‏‏‎‏‏‏‏‏‎‏‎‎‎‎‏‏‎‏‎Playing on ‎‏‎‎‏‏‎<xliff:g id="DEVICENAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‏‎‎‎‎‎‏‎‏‏‎‎‎‏‏‏‎‎‎‏‏‏‎‏‎‎‎‏‎‏‎‏‎‎‏‏‏‏‎‏‏‎‏‎‎‎‎‏‎‎‏‏‏‏‏‏‎‎Inactive, check app‎‏‎‎‏‎"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‎‏‎‏‎‎‏‎‎‏‎‏‎‎‎‎‏‎‏‏‏‎‏‎‎‏‎‎‏‏‎‏‎‎‎‏‏‏‏‏‎‏‏‏‏‏‎‎‏‏‎‎‏‏‏‏‎‎Not found‎‏‎‎‏‎"</string>
@@ -879,4 +879,6 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‎‎‏‎‎‎‎‏‎‏‎‏‎‏‏‏‏‏‎‎‎‎‏‏‏‎‏‎‎‎‏‏‎‎‎‎‏‏‏‏‏‎‏‎‏‎‏‏‎‏‏‏‏‎‏‏‎‎Select user‎‏‎‎‏‎"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‏‎‎‎‏‏‎‏‎‏‏‏‎‏‎‎‎‎‎‎‎‏‎‏‏‏‏‏‎‏‎‎‏‏‏‏‏‎‏‏‎‎‎‏‎‏‎‏‏‏‏‏‎‏‏‎‏‎Apps running in the background‎‏‎‎‏‎"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‏‏‏‎‎‎‎‏‎‎‎‎‎‏‎‎‏‎‎‎‏‏‎‏‏‏‎‎‎‎‏‎‎‎‎‎‎‏‎‏‎‏‏‎‏‎‎‎‎‏‎‎‎‎‏‎‎‎Stop‎‏‎‎‏‎"</string>
+ <string name="clipboard_edit_text_copy" msgid="770856373439969178">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‎‏‎‏‏‎‎‏‎‏‎‏‎‎‎‎‏‏‏‎‎‏‎‎‏‏‎‎‏‎‏‏‏‏‎‏‏‏‏‎‎‎‎‏‏‎‏‏‏‏‎‎‏‏‎‏‎‎Copy‎‏‎‎‏‎"</string>
+ <string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‏‏‏‏‏‏‏‎‎‏‏‏‎‎‏‎‏‏‏‏‏‏‎‎‎‎‏‏‏‏‎‏‏‎‏‏‏‎‎‎‎‎‏‎‎‏‏‎‏‏‏‏‎‎‎‏‏‎Copied‎‏‎‎‏‎"</string>
</resources>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 66bbbbee56dc..f748e9dce65a 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -233,8 +233,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Red Wi-Fi no conectada"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Brillo"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inversión de color"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Corrección de colores"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Más configuraciones"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Configuración del usuario"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Listo"</string>
@@ -450,8 +449,9 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Desbloquear para usar"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Ocurrió un problema al obtener las tarjetas; vuelve a intentarlo más tarde"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Configuración de pantalla de bloqueo"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"Escanear QR"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"Haz clic para escanear un código QR"</string>
+ <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+ <skip />
+ <string name="qr_code_scanner_description" msgid="7937603775306661863">"Presiona para escanear"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Perfil de trabajo"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Modo de avión"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"No oirás la próxima alarma a la(s) <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -792,7 +792,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Reproduce <xliff:g id="SONG_NAME">%1$s</xliff:g>, de <xliff:g id="ARTIST_NAME">%2$s</xliff:g>, en <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Reproducir <xliff:g id="SONG_NAME">%1$s</xliff:g> en <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Deshacer"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Acércate para reproducir en <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"Reproduciendo en <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Inactivo. Verifica la app"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"No se encontró"</string>
@@ -880,4 +881,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Seleccionar usuario"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"Apps en ejecución en segundo plano"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Detener"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-es-rUS/tiles_states_strings.xml b/packages/SystemUI/res/values-es-rUS/tiles_states_strings.xml
index 0d77977fd174..6e425eea6542 100644
--- a/packages/SystemUI/res/values-es-rUS/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"Desactivado"</item>
<item msgid="2075645297847971154">"Activado"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"No disponible"</item>
+ <item msgid="1909756493418256167">"Desactivada"</item>
+ <item msgid="4531508423703413340">"Activada"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"No disponible"</item>
<item msgid="9103697205127645916">"Desactivado"</item>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 98b45d5162e0..04947803c4bc 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -233,8 +233,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi‑Fi no conectado"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Brillo"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Invertir colores"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Corrección de color"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Más ajustes"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Ajustes de usuario"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Hecho"</string>
@@ -450,8 +449,10 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Desbloquear para usar"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Se ha producido un problema al obtener tus tarjetas. Inténtalo de nuevo más tarde."</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Ajustes de pantalla de bloqueo"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"Escanear QR"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"Haz clic para escanear un código QR"</string>
+ <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+ <skip />
+ <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+ <skip />
<string name="status_bar_work" msgid="5238641949837091056">"Perfil de trabajo"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Modo avión"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"No oirás la próxima alarma (<xliff:g id="WHEN">%1$s</xliff:g>)"</string>
@@ -792,7 +793,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Poner <xliff:g id="SONG_NAME">%1$s</xliff:g> de <xliff:g id="ARTIST_NAME">%2$s</xliff:g> en <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Poner <xliff:g id="SONG_NAME">%1$s</xliff:g> en <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Deshacer"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Acércate a <xliff:g id="DEVICENAME">%1$s</xliff:g> para que se reproduzca en ese dispositivo"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"Reproduciendo en <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Inactivo, comprobar aplicación"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"No se ha encontrado"</string>
@@ -880,4 +882,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Seleccionar usuario"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"Aplicaciones ejecutándose en segundo plano"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Detener"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-es/tiles_states_strings.xml b/packages/SystemUI/res/values-es/tiles_states_strings.xml
index 080731a2feb1..3ac10ec4a2cf 100644
--- a/packages/SystemUI/res/values-es/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-es/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"Desactivado"</item>
<item msgid="2075645297847971154">"Activado"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"No disponible"</item>
+ <item msgid="1909756493418256167">"Desactivada"</item>
+ <item msgid="4531508423703413340">"Activada"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"No disponible"</item>
<item msgid="9103697205127645916">"Desactivado"</item>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index 7526b0dea7f8..15c10be74efc 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -233,8 +233,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"WiFi-ühendus puudub"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Heledus"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Värvide ümberpööramine"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Värviparandus"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Rohkem seadeid"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Kasutaja seaded"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Valmis"</string>
@@ -450,8 +449,10 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Avage kasutamiseks"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Teie kaartide hankimisel ilmnes probleem, proovige hiljem uuesti"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Lukustuskuva seaded"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR-koodi skannimine"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"Klõpsake QR-koodi skannimiseks"</string>
+ <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+ <skip />
+ <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+ <skip />
<string name="status_bar_work" msgid="5238641949837091056">"Tööprofiil"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Lennukirežiim"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Te ei kuule järgmist äratust kell <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -792,7 +793,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Esita lugu <xliff:g id="SONG_NAME">%1$s</xliff:g> esitajalt <xliff:g id="ARTIST_NAME">%2$s</xliff:g> rakenduses <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Esita lugu <xliff:g id="SONG_NAME">%1$s</xliff:g> rakenduses <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Võta tagasi"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Minge lähemale, et seadmes <xliff:g id="DEVICENAME">%1$s</xliff:g> esitada"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"Esitatakse seadmes <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Passiivne, vaadake rakendust"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Ei leitud"</string>
@@ -880,4 +882,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Kasutaja valimine"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"Taustal töötavad rakendused"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Peata"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-et/tiles_states_strings.xml b/packages/SystemUI/res/values-et/tiles_states_strings.xml
index 26d71fe07a57..27edd17c9b0d 100644
--- a/packages/SystemUI/res/values-et/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-et/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"Väljas"</item>
<item msgid="2075645297847971154">"Sees"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"Pole saadaval"</item>
+ <item msgid="1909756493418256167">"Väljas"</item>
+ <item msgid="4531508423703413340">"Sees"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"Pole saadaval"</item>
<item msgid="9103697205127645916">"Väljas"</item>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 71c3feb8594c..454ad59807bb 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -233,8 +233,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Ez zaude konektatuta wifi-sarera"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Distira"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Kolore-alderantzikatzea"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Koloreen zuzenketa"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Ezarpen gehiago"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Erabiltzaile-ezarpenak"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Eginda"</string>
@@ -450,8 +449,9 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Desblokeatu erabiltzeko"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Arazo bat izan da txartelak eskuratzean. Saiatu berriro geroago."</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Pantaila blokeatuaren ezarpenak"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"Eskaneatu QR kode bat"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"QR kode bat eskaneatzeko, sakatu hau"</string>
+ <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+ <skip />
+ <string name="qr_code_scanner_description" msgid="7937603775306661863">"Sakatu eskaneatzeko"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Work profila"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Hegaldi modua"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Ez duzu entzungo hurrengo alarma (<xliff:g id="WHEN">%1$s</xliff:g>)"</string>
@@ -792,7 +792,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Erreproduzitu <xliff:g id="SONG_NAME">%1$s</xliff:g> (<xliff:g id="ARTIST_NAME">%2$s</xliff:g>) <xliff:g id="APP_LABEL">%3$s</xliff:g> bidez"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Erreproduzitu <xliff:g id="SONG_NAME">%1$s</xliff:g> <xliff:g id="APP_LABEL">%2$s</xliff:g> bidez"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Desegin"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Gerturatu <xliff:g id="DEVICENAME">%1$s</xliff:g> gailuan erreproduzitzeko"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"<xliff:g id="DEVICENAME">%1$s</xliff:g> pantailan erreproduzitzen"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Inaktibo; egiaztatu aplikazioa"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Ez da aurkitu"</string>
@@ -880,4 +881,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Hautatu erabiltzaile bat"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"Atzeko planoan exekutatzen ari diren aplikazioak"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Gelditu"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-eu/tiles_states_strings.xml b/packages/SystemUI/res/values-eu/tiles_states_strings.xml
index 11090458f1f5..eb13a1202cc4 100644
--- a/packages/SystemUI/res/values-eu/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-eu/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"Desaktibatuta"</item>
<item msgid="2075645297847971154">"Aktibatuta"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"Ez dago erabilgarri"</item>
+ <item msgid="1909756493418256167">"Desaktibatuta"</item>
+ <item msgid="4531508423703413340">"Aktibatuta"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"Ez dago erabilgarri"</item>
<item msgid="9103697205127645916">"Desaktibatuta"</item>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 4efdc8428097..c2c1ffb3ca48 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -233,8 +233,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"‏Wi-Fi وصل نیست"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"روشنایی"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"وارونگی رنگ"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"تصحیح رنگ"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"تنظیمات بیشتر"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"تنظیمات کاربر"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"تمام"</string>
@@ -450,8 +449,10 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"برای استفاده، قفل را باز کنید"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"هنگام دریافت کارت‌ها مشکلی پیش آمد، لطفاً بعداً دوباره امتحان کنید"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"تنظیمات صفحه قفل"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"اسکن رمزینه پاسخ‌سریع"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"برای اسکن رمزینه پاسخ‌سریع، کلیک کنید"</string>
+ <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+ <skip />
+ <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+ <skip />
<string name="status_bar_work" msgid="5238641949837091056">"نمایه کاری"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"حالت هواپیما"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"در ساعت <xliff:g id="WHEN">%1$s</xliff:g>، دیگر صدای زنگ ساعت را نمی‌شنوید"</string>
@@ -792,7 +793,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="SONG_NAME">%1$s</xliff:g> از <xliff:g id="ARTIST_NAME">%2$s</xliff:g> را ازطریق <xliff:g id="APP_LABEL">%3$s</xliff:g> پخش کنید"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> را ازطریق <xliff:g id="APP_LABEL">%2$s</xliff:g> پخش کنید"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"واگرد"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"برای پخش در <xliff:g id="DEVICENAME">%1$s</xliff:g>، به دستگاه نزدیک‌تر شوید"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"درحال پخش در <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"غیرفعال، برنامه را بررسی کنید"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"پیدا نشد"</string>
@@ -880,4 +882,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"انتخاب کاربر"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"برنامه‌هایی که در پس‌زمینه اجرا می‌شود"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"توقف"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-fa/tiles_states_strings.xml b/packages/SystemUI/res/values-fa/tiles_states_strings.xml
index ab755197d0a5..dcde4d3e18db 100644
--- a/packages/SystemUI/res/values-fa/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-fa/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"خاموش"</item>
<item msgid="2075645297847971154">"روشن"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"دردسترس نیست"</item>
+ <item msgid="1909756493418256167">"خاموش"</item>
+ <item msgid="4531508423703413340">"روشن"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"دردسترس نیست"</item>
<item msgid="9103697205127645916">"خاموش"</item>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 1ab196f09aca..cd3844fad9da 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -100,7 +100,7 @@
<string name="accessibility_back" msgid="6530104400086152611">"Takaisin"</string>
<string name="accessibility_home" msgid="5430449841237966217">"Aloitus"</string>
<string name="accessibility_menu" msgid="2701163794470513040">"Valikko"</string>
- <string name="accessibility_accessibility_button" msgid="4089042473497107709">"Esteettömyys"</string>
+ <string name="accessibility_accessibility_button" msgid="4089042473497107709">"Saavutettavuus"</string>
<string name="accessibility_rotate_button" msgid="1238584767612362586">"Näytön kääntäminen"</string>
<string name="accessibility_recent" msgid="901641734769533575">"Viimeisimmät"</string>
<string name="accessibility_camera_button" msgid="2938898391716647247">"Kamera"</string>
@@ -233,8 +233,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fiä ei ole yhdistetty"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Kirkkaus"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Käänteiset värit"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Värinkorjaus"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Lisäasetukset"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Käyttäjäasetukset"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Valmis"</string>
@@ -450,8 +449,10 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Avaa lukitus ja käytä"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Korttien noutamisessa oli ongelma, yritä myöhemmin uudelleen"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Lukitusnäytön asetukset"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"Skannaa QR-koodi"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"Skannaa QR-koodi klikkaamalla"</string>
+ <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+ <skip />
+ <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+ <skip />
<string name="status_bar_work" msgid="5238641949837091056">"Työprofiili"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Lentokonetila"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Et kuule seuraavaa hälytystäsi (<xliff:g id="WHEN">%1$s</xliff:g>)."</string>
@@ -792,7 +793,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Soita <xliff:g id="SONG_NAME">%1$s</xliff:g> (<xliff:g id="ARTIST_NAME">%2$s</xliff:g>) sovelluksessa <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Soita <xliff:g id="SONG_NAME">%1$s</xliff:g> (<xliff:g id="APP_LABEL">%2$s</xliff:g>)"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Kumoa"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Siirry lähemmäs, jotta <xliff:g id="DEVICENAME">%1$s</xliff:g> voi toistaa tämän"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"<xliff:g id="DEVICENAME">%1$s</xliff:g> toistaa tämän"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Epäaktiivinen, tarkista sovellus"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Ei löydy"</string>
@@ -880,4 +882,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Valitse käyttäjä"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"Sovellukset jotka ovat käynnissä taustalla"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Lopeta"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-fi/tiles_states_strings.xml b/packages/SystemUI/res/values-fi/tiles_states_strings.xml
index 6fa9466e1313..d838cf84d409 100644
--- a/packages/SystemUI/res/values-fi/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-fi/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"Poissa päältä"</item>
<item msgid="2075645297847971154">"Päällä"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"Ei saatavilla"</item>
+ <item msgid="1909756493418256167">"Pois päältä"</item>
+ <item msgid="4531508423703413340">"Päällä"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"Ei saatavilla"</item>
<item msgid="9103697205127645916">"Poissa päältä"</item>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 4eadcc232a73..440369d54ea1 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -233,8 +233,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Non connecté au Wi-Fi"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Luminosité"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inversion des couleurs"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Correction des couleurs"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Plus de paramètres"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Paramètres utilisateur"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Terminé"</string>
@@ -450,8 +449,10 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Déverrouiller pour utiliser"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Un problème est survenu lors de la récupération de vos cartes, veuillez réessayer plus tard"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Paramètres de l\'écran de verrouillage"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"Numériser le code QR"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"Cliquez pour numériser un code QR"</string>
+ <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+ <skip />
+ <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+ <skip />
<string name="status_bar_work" msgid="5238641949837091056">"Profil professionnel"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Mode Avion"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Vous n\'entendrez pas votre prochaine alarme à <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -792,7 +793,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Lecture de <xliff:g id="SONG_NAME">%1$s</xliff:g> par <xliff:g id="ARTIST_NAME">%2$s</xliff:g> à partir de <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Lecture de <xliff:g id="SONG_NAME">%1$s</xliff:g> à partir de <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Annuler"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Rapprochez-vous pour faire jouer le contenu sur <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"Lecture sur <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Délai expiré, vérifiez l\'appli"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Introuvable"</string>
@@ -880,4 +882,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Sélect. utilisateur"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"Applications exécutées en arrière-plan"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Arrêter"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml b/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml
index aec8ab87dcfb..0b087ad821c8 100644
--- a/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"Désactivé"</item>
<item msgid="2075645297847971154">"Activé"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"Non disponible"</item>
+ <item msgid="1909756493418256167">"Désactivée"</item>
+ <item msgid="4531508423703413340">"Activée"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"Non disponible"</item>
<item msgid="9103697205127645916">"Désactivée"</item>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 33fc2945a045..891e85c19724 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -233,8 +233,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi non connecté"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Luminosité"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inversion des couleurs"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Correction des couleurs"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Plus de paramètres"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Paramètres utilisateur"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"OK"</string>
@@ -450,8 +449,9 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Déverrouiller pour utiliser"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Problème de récupération de vos cartes. Réessayez plus tard"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Paramètres de l\'écran de verrouillage"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"Scanner un code QR"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"Cliquer pour scanner un code QR"</string>
+ <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+ <skip />
+ <string name="qr_code_scanner_description" msgid="7937603775306661863">"Appuyer pour scanner"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Profil professionnel"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Mode Avion"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Vous n\'entendrez pas votre prochaine alarme <xliff:g id="WHEN">%1$s</xliff:g>."</string>
@@ -792,7 +792,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Mets <xliff:g id="SONG_NAME">%1$s</xliff:g> par <xliff:g id="ARTIST_NAME">%2$s</xliff:g> depuis <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Mets <xliff:g id="SONG_NAME">%1$s</xliff:g> depuis <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Annuler"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Rapprochez-vous pour lire sur <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"Lecture sur <xliff:g id="DEVICENAME">%1$s</xliff:g>…"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Délai expiré, vérifier l\'appli"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Introuvable"</string>
@@ -880,4 +881,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Choisir utilisateur"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"Applis exécutées en arrière-plan"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Arrêter"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-fr/tiles_states_strings.xml b/packages/SystemUI/res/values-fr/tiles_states_strings.xml
index 01d9c1d74dd0..fbae02afb9b5 100644
--- a/packages/SystemUI/res/values-fr/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-fr/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"Désactivé"</item>
<item msgid="2075645297847971154">"Activé"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"Indisponible"</item>
+ <item msgid="1909756493418256167">"Désactivée"</item>
+ <item msgid="4531508423703413340">"Activée"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"Indisponible"</item>
<item msgid="9103697205127645916">"Désactivée"</item>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 40fe254b563e..70a3c68ee29f 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -233,8 +233,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"A wifi non está conectada"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Brillo"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inversión da cor"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Corrección da cor"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Máis opcións"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Configuración de usuario"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Feito"</string>
@@ -450,8 +449,10 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Desbloquear para usar"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Produciuse un problema ao obter as tarxetas. Téntao de novo máis tarde"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Configuración da pantalla de bloqueo"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"Escanear QR"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"Fai clic para escanear un código QR"</string>
+ <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+ <skip />
+ <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+ <skip />
<string name="status_bar_work" msgid="5238641949837091056">"Perfil de traballo"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Modo avión"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Non escoitarás a alarma seguinte <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -792,7 +793,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Reproduce <xliff:g id="SONG_NAME">%1$s</xliff:g>, de <xliff:g id="ARTIST_NAME">%2$s</xliff:g>, en <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Reproduce <xliff:g id="SONG_NAME">%1$s</xliff:g> en <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Desfacer"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Achega o dispositivo para reproducir a música en: <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"Estase reproducindo o contido en: <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Inactivo. Comproba a app"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Non se atopou"</string>
@@ -880,4 +882,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Seleccionar usuario"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"Aplicacións que se están executando en segundo plano"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Deter"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-gl/tiles_states_strings.xml b/packages/SystemUI/res/values-gl/tiles_states_strings.xml
index 9045983425df..531e7ff88e4f 100644
--- a/packages/SystemUI/res/values-gl/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-gl/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"Non"</item>
<item msgid="2075645297847971154">"Si"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"Non dispoñible"</item>
+ <item msgid="1909756493418256167">"Desactivada"</item>
+ <item msgid="4531508423703413340">"Activada"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"Non dispoñible"</item>
<item msgid="9103697205127645916">"Non"</item>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index da52c40e505a..5f262b57e67c 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -233,8 +233,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"વાઇ-ફાઇ કનેક્ટ નથી"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"તેજ"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"વિપરીત રંગમાં બદલવું"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"રંગ સુધારણા"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"વધુ સેટિંગ"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"વપરાશકર્તા સેટિંગ"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"થઈ ગયું"</string>
@@ -450,8 +449,9 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"ઉપયોગ કરવા માટે અનલૉક કરો"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"તમારા કાર્ડની માહિતી મેળવવામાં સમસ્યા આવી હતી, કૃપા કરીને થોડા સમય પછી ફરી પ્રયાસ કરો"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"લૉક સ્ક્રીનના સેટિંગ"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR સ્કૅન કરો"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"QR કોડ સ્કૅન કરવા માટે ક્લિક કરો"</string>
+ <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+ <skip />
+ <string name="qr_code_scanner_description" msgid="7937603775306661863">"સ્કૅન કરવા માટે ટૅપ કરો"</string>
<string name="status_bar_work" msgid="5238641949837091056">"ઑફિસની પ્રોફાઇલ"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"એરપ્લેન મોડ"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"તમે <xliff:g id="WHEN">%1$s</xliff:g> એ તમારો આગલો એલાર્મ સાંભળશો નહીં"</string>
@@ -792,7 +792,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="APP_LABEL">%3$s</xliff:g> પર <xliff:g id="ARTIST_NAME">%2$s</xliff:g>નું <xliff:g id="SONG_NAME">%1$s</xliff:g> ગીત ચલાવો"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="APP_LABEL">%2$s</xliff:g> પર <xliff:g id="SONG_NAME">%1$s</xliff:g> ગીત ચલાવો"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"છેલ્લો ફેરફાર રદ કરો"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"<xliff:g id="DEVICENAME">%1$s</xliff:g> પર ચલાવવા માટે વધુ નજીક ખસેડો"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"<xliff:g id="DEVICENAME">%1$s</xliff:g> પર ચલાવવામાં આવી રહ્યું છે"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"નિષ્ક્રિય, ઍપને ચેક કરો"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"મળ્યું નથી"</string>
@@ -880,4 +881,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"વપરાશકર્તા પસંદ કરો"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"બૅકગ્રાઉન્ડમાં ચાલતી ઍપ"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"રોકો"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-gu/tiles_states_strings.xml b/packages/SystemUI/res/values-gu/tiles_states_strings.xml
index 15b0f9fe7fbe..10e7ac7b7de0 100644
--- a/packages/SystemUI/res/values-gu/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-gu/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"બંધ છે"</item>
<item msgid="2075645297847971154">"ચાલુ છે"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"અનુપલબ્ધ"</item>
+ <item msgid="1909756493418256167">"બંધ છે"</item>
+ <item msgid="4531508423703413340">"ચાલુ છે"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"ઉપલબ્ધ નથી"</item>
<item msgid="9103697205127645916">"બંધ છે"</item>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 54b73897332d..755c4805be36 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -233,8 +233,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"वाई-फ़ाई कनेक्ट नहीं है"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"स्क्रीन की रोशनी"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"रंग बदलने की सुविधा"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"रंग में सुधार करने की सुविधा"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"और सेटिंग"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"उपयोगकर्ता सेटिंग"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"हो गया"</string>
@@ -450,8 +449,10 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"इस्तेमाल करने के लिए, डिवाइस अनलॉक करें"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"आपके कार्ड की जानकारी पाने में कोई समस्या हुई है. कृपया बाद में कोशिश करें"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"लॉक स्क्रीन की सेटिंग"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"क्यूआर कोड स्कैन करें"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"क्यूआर कोड स्कैन करने के लिए क्लिक करें"</string>
+ <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+ <skip />
+ <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+ <skip />
<string name="status_bar_work" msgid="5238641949837091056">"वर्क प्रोफ़ाइल"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"हवाई जहाज़ मोड"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"आपको <xliff:g id="WHEN">%1$s</xliff:g> पर अपना अगला अलार्म नहीं सुनाई देगा"</string>
@@ -792,7 +793,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="APP_LABEL">%3$s</xliff:g> पर, <xliff:g id="ARTIST_NAME">%2$s</xliff:g> का <xliff:g id="SONG_NAME">%1$s</xliff:g> चलाएं"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="APP_LABEL">%2$s</xliff:g> पर, <xliff:g id="SONG_NAME">%1$s</xliff:g> चलाएं"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"पहले जैसा करें"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"<xliff:g id="DEVICENAME">%1$s</xliff:g> पर गाने चलाने के लिए उसके पास जाएं"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"<xliff:g id="DEVICENAME">%1$s</xliff:g> पर चल रहा है"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"काम नहीं कर रहा, ऐप जांचें"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"कंट्रोल नहीं है"</string>
@@ -880,4 +882,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"उपयोगकर्ता चुनें"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"बैकग्राउंड में चल रहे ऐप्लिकेशन"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"बंद करें"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-hi/tiles_states_strings.xml b/packages/SystemUI/res/values-hi/tiles_states_strings.xml
index 00fa69936dc6..e52ee17c5100 100644
--- a/packages/SystemUI/res/values-hi/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-hi/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"बंद है"</item>
<item msgid="2075645297847971154">"चालू है"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"उपलब्ध नहीं है"</item>
+ <item msgid="1909756493418256167">"बंद है"</item>
+ <item msgid="4531508423703413340">"चालू है"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"उपलब्ध नहीं है"</item>
<item msgid="9103697205127645916">"बंद है"</item>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 8931d20dda9a..a5be1e360589 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -234,8 +234,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi mreža nije povezana"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Svjetlina"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inverzija boja"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Korekcija boja"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Više postavki"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Korisničke postavke"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Gotovo"</string>
@@ -453,8 +452,10 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Otključajte da biste koristili"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Pojavio se problem prilikom dohvaćanja kartica, pokušajte ponovo kasnije"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Postavke zaključanog zaslona"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"Skeniraj QR kôd"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"Kliknite da biste skenirali QR kôd"</string>
+ <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+ <skip />
+ <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+ <skip />
<string name="status_bar_work" msgid="5238641949837091056">"Poslovni profil"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Način rada u zrakoplovu"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Nećete čuti sljedeći alarm <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -798,7 +799,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Pustite <xliff:g id="SONG_NAME">%1$s</xliff:g>, <xliff:g id="ARTIST_NAME">%2$s</xliff:g> putem aplikacije <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Pustite <xliff:g id="SONG_NAME">%1$s</xliff:g> putem aplikacije <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Poništi"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Približite se radi reprodukcije na uređaju <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"Reproducira se na uređaju <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Neaktivno, provjerite aplik."</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Nije pronađeno"</string>
@@ -886,4 +888,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Odabir korisnika"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"Aplikacije koje se izvode u pozadini"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Zaustavi"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-hr/tiles_states_strings.xml b/packages/SystemUI/res/values-hr/tiles_states_strings.xml
index 730081667113..eb9ae525fa2e 100644
--- a/packages/SystemUI/res/values-hr/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-hr/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"Isključeno"</item>
<item msgid="2075645297847971154">"Uključeno"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"Nedostupno"</item>
+ <item msgid="1909756493418256167">"Isključeno"</item>
+ <item msgid="4531508423703413340">"Uključeno"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"Nedostupno"</item>
<item msgid="9103697205127645916">"Isključeno"</item>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index eff9e8222e9e..0275b729ad31 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -233,8 +233,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Nem kapcsolódik Wi‑Fi-hálózathoz"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Fényerő"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Színek invertálása"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Színjavítás"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"További beállítások"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Felhasználói beállítások"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Kész"</string>
@@ -450,8 +449,10 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Oldja fel a használathoz"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Probléma merült fel a kártyák lekérésekor, próbálja újra később"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Lezárási képernyő beállításai"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR-kód beolvasása"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"Kattintson a QR-kód beolvasához"</string>
+ <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+ <skip />
+ <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+ <skip />
<string name="status_bar_work" msgid="5238641949837091056">"Munkahelyi profil"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Repülős üzemmód"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Nem fogja hallani az ébresztést ekkor: <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -792,7 +793,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="ARTIST_NAME">%2$s</xliff:g> <xliff:g id="SONG_NAME">%1$s</xliff:g> című számának lejátszása innen: <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> lejátszása innen: <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Visszavonás"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Menjen közelebb a következőn való lejátszáshoz: <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"Lejátszás folyamatban a(z) <xliff:g id="DEVICENAME">%1$s</xliff:g> eszközön"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Inaktív, ellenőrizze az appot"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Nem található"</string>
@@ -880,4 +882,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Felhasználóválasztás"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"Több alkalmazás is fut a háttérben"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Leállítás"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-hu/tiles_states_strings.xml b/packages/SystemUI/res/values-hu/tiles_states_strings.xml
index 52450e4b3937..ba92bfd3bf74 100644
--- a/packages/SystemUI/res/values-hu/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-hu/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"Ki"</item>
<item msgid="2075645297847971154">"Be"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"Nem áll rendelkezésre"</item>
+ <item msgid="1909756493418256167">"Ki"</item>
+ <item msgid="4531508423703413340">"Be"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"Nem áll rendelkezésre"</item>
<item msgid="9103697205127645916">"Ki"</item>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 084b309e3751..c32b16686eba 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -233,8 +233,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi-ը միացված չէ"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Պայծառություն"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Գունաշրջում"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Գունաշտկում"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Հավելյալ կարգավորումներ"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Օգտատիրոջ կարգավորումներ"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Պատրաստ է"</string>
@@ -450,8 +449,9 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Ապակողպել՝ օգտագործելու համար"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Չհաջողվեց բեռնել քարտերը։ Նորից փորձեք։"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Կողպէկրանի կարգավորումներ"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR կոդերի սկանավորում"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"Սեղմեք՝ QR կոդը սկանավորելու համար"</string>
+ <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+ <skip />
+ <string name="qr_code_scanner_description" msgid="7937603775306661863">"Հպեք՝ սկանավորելու համար"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Android for Work-ի պրոֆիլ"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Ավիառեժիմ"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Ժամը <xliff:g id="WHEN">%1$s</xliff:g>-ի զարթուցիչը չի զանգի"</string>
@@ -792,7 +792,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Նվագարկել <xliff:g id="SONG_NAME">%1$s</xliff:g> երգը <xliff:g id="ARTIST_NAME">%2$s</xliff:g>-ի կատարմամբ <xliff:g id="APP_LABEL">%3$s</xliff:g> հավելվածից"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Նվագարկել <xliff:g id="SONG_NAME">%1$s</xliff:g> երգը <xliff:g id="APP_LABEL">%2$s</xliff:g> հավելվածից"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Հետարկել"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Ավելի մոտ եկեք՝ <xliff:g id="DEVICENAME">%1$s</xliff:g> սարքում նվագարկելու համար"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"Նվագարկվում է <xliff:g id="DEVICENAME">%1$s</xliff:g> սարքում"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Ակտիվ չէ, ստուգեք հավելվածը"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Չի գտնվել"</string>
@@ -880,4 +881,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Ընտրեք օգտատեր"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"Ֆոնային ռեժիմում աշխատող հավելվածներ"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Դադարեցնել"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-hy/tiles_states_strings.xml b/packages/SystemUI/res/values-hy/tiles_states_strings.xml
index 23a096b194af..b52646f352b5 100644
--- a/packages/SystemUI/res/values-hy/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-hy/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"Անջատված է"</item>
<item msgid="2075645297847971154">"Միացված է"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"Անհասանելի է"</item>
+ <item msgid="1909756493418256167">"Անջատված է"</item>
+ <item msgid="4531508423703413340">"Միացված է"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"Հասանելի չէ"</item>
<item msgid="9103697205127645916">"Անջատված է"</item>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index e595045e913b..24f8698cf7ee 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -233,8 +233,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi‑Fi tidak terhubung"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Kecerahan"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inversi warna"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Koreksi warna"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Setelan lainnya"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Setelan pengguna"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Selesai"</string>
@@ -450,8 +449,10 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Buka kunci untuk menggunakan"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Terjadi masalah saat mendapatkan kartu Anda, coba lagi nanti"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Setelan layar kunci"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"Pindai QR"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"Klik untuk memindai kode QR"</string>
+ <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+ <skip />
+ <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+ <skip />
<string name="status_bar_work" msgid="5238641949837091056">"Profil kerja"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Mode pesawat"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Anda tidak akan mendengar alarm berikutnya <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -792,7 +793,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Putar <xliff:g id="SONG_NAME">%1$s</xliff:g> oleh <xliff:g id="ARTIST_NAME">%2$s</xliff:g> dari <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Putar <xliff:g id="SONG_NAME">%1$s</xliff:g> dari <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Urungkan"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Dekatkan untuk memutar di <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"Diputar di <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Nonaktif, periksa aplikasi"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Tidak ditemukan"</string>
@@ -880,4 +882,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Pilih pengguna"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"Aplikasi berjalan di latar belakang"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Berhenti"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-in/tiles_states_strings.xml b/packages/SystemUI/res/values-in/tiles_states_strings.xml
index c296e5443190..0007dfc3b581 100644
--- a/packages/SystemUI/res/values-in/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-in/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"Nonaktif"</item>
<item msgid="2075645297847971154">"Aktif"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"Tidak tersedia"</item>
+ <item msgid="1909756493418256167">"Nonaktif"</item>
+ <item msgid="4531508423703413340">"Aktif"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"Tidak tersedia"</item>
<item msgid="9103697205127645916">"Nonaktif"</item>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index 43ae1b651f0e..a7a43e6f7562 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -233,8 +233,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi ekki tengt"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Birtustig"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Umsnúningur lita"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Litaleiðrétting"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Fleiri stillingar"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Notandastillingar"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Lokið"</string>
@@ -450,8 +449,10 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Taktu úr lás til að nota"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Vandamál kom upp við að sækja kortin þín. Reyndu aftur síðar"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Stillingar fyrir læstan skjá"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"Skanna QR-kóða"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"Smelltu til að skanna QR-kóða"</string>
+ <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+ <skip />
+ <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+ <skip />
<string name="status_bar_work" msgid="5238641949837091056">"Vinnusnið"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Flugstilling"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Ekki mun heyrast í vekjaranum <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -792,7 +793,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Spila <xliff:g id="SONG_NAME">%1$s</xliff:g> með <xliff:g id="ARTIST_NAME">%2$s</xliff:g> í <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Spila <xliff:g id="SONG_NAME">%1$s</xliff:g> í <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Afturkalla"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Færðu nær til að spila í <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"Spilast í <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Óvirkt, athugaðu forrit"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Fannst ekki"</string>
@@ -880,4 +882,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Velja notanda"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"Forrit keyra í bakgrunni"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Stöðva"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-is/tiles_states_strings.xml b/packages/SystemUI/res/values-is/tiles_states_strings.xml
index 25b3dcc53661..88472ef4b2fc 100644
--- a/packages/SystemUI/res/values-is/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-is/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"Slökkt"</item>
<item msgid="2075645297847971154">"Kveikt"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"Ekki í boði"</item>
+ <item msgid="1909756493418256167">"Slökkt"</item>
+ <item msgid="4531508423703413340">"Kveikt"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"Ekki í boði"</item>
<item msgid="9103697205127645916">"Slökkt"</item>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 00d7fb35d15e..2f3d50ff1816 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -233,8 +233,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Nessuna connessione Wi-Fi"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Luminosità"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inversione dei colori"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Correzione del colore"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Altre impostazioni"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Impostazioni utente"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Fine"</string>
@@ -450,8 +449,9 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Sblocca per usare"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Si è verificato un problema durante il recupero delle tue carte. Riprova più tardi."</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Impostazioni schermata di blocco"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"Scansiona QR"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"Fai clic per scansionare un codice QR"</string>
+ <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+ <skip />
+ <string name="qr_code_scanner_description" msgid="7937603775306661863">"Tocca per scansionare"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Profilo di lavoro"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Modalità aereo"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Non sentirai la tua prossima sveglia <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -792,7 +792,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Riproduci <xliff:g id="SONG_NAME">%1$s</xliff:g> di <xliff:g id="ARTIST_NAME">%2$s</xliff:g> da <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Riproduci <xliff:g id="SONG_NAME">%1$s</xliff:g> da <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Annulla"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Avvicinati per riprodurre su <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"In riproduzione su <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Inattivo, controlla l\'app"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Controllo non trovato"</string>
@@ -880,4 +881,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Seleziona utente"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"App in esecuzione in background"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Interrompi"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-it/tiles_states_strings.xml b/packages/SystemUI/res/values-it/tiles_states_strings.xml
index 7e6827a4d8c2..071a970d2260 100644
--- a/packages/SystemUI/res/values-it/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-it/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"Off"</item>
<item msgid="2075645297847971154">"On"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"Non disponibile"</item>
+ <item msgid="1909756493418256167">"Off"</item>
+ <item msgid="4531508423703413340">"On"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"Non disponibile"</item>
<item msgid="9103697205127645916">"Off"</item>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index b150eb7e6757..757ff778a018 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -235,8 +235,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"‏אין חיבור ל-Wi-Fi"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"בהירות"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"היפוך צבעים"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"תיקון צבע"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"הגדרות נוספות"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"הגדרות המשתמש"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"בוצע"</string>
@@ -456,8 +455,10 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"יש לבטל את הנעילה כדי להשתמש"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"הייתה בעיה בקבלת הכרטיסים שלך. כדאי לנסות שוב מאוחר יותר"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"הגדרות מסך הנעילה"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"‏סריקת קוד QR"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"‏צריך ללחוץ כאן כדי לסרוק קוד QR"</string>
+ <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+ <skip />
+ <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+ <skip />
<string name="status_bar_work" msgid="5238641949837091056">"פרופיל עבודה"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"מצב טיסה"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"לא ניתן יהיה לשמוע את ההתראה הבאה שלך <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -804,7 +805,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"הפעלת <xliff:g id="SONG_NAME">%1$s</xliff:g> של <xliff:g id="ARTIST_NAME">%2$s</xliff:g> מ-<xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"הפעלת <xliff:g id="SONG_NAME">%1$s</xliff:g> מ-<xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"ביטול"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"צריך להתקרב כדי להפעיל מוזיקה במכשיר <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"ההפעלה הועברה למכשיר <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"לא פעיל, יש לבדוק את האפליקציה"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"לא נמצא"</string>
@@ -892,4 +894,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"בחירת משתמש"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"אפליקציות שפועלות ברקע"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"עצירה"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-iw/tiles_states_strings.xml b/packages/SystemUI/res/values-iw/tiles_states_strings.xml
index bf3c2b6ad767..49fb4b671546 100644
--- a/packages/SystemUI/res/values-iw/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-iw/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"כבוי"</item>
<item msgid="2075645297847971154">"פועל"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"לא זמין"</item>
+ <item msgid="1909756493418256167">"כבוי"</item>
+ <item msgid="4531508423703413340">"פועל"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"לא זמין"</item>
<item msgid="9103697205127645916">"כבוי"</item>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 1dde2100db8d..f219de3824b8 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -233,8 +233,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi 未接続"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"画面の明るさ"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"色反転"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"色補正"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"詳細設定"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"ユーザー設定"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"完了"</string>
@@ -450,8 +449,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"ロックを解除して使用"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"カードの取得中に問題が発生しました。しばらくしてからもう一度お試しください"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"ロック画面の設定"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR のスキャン"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"クリックすると、QR コードをスキャンします"</string>
+ <string name="qr_code_scanner_title" msgid="5660820608548306581">"QR コード"</string>
+ <string name="qr_code_scanner_description" msgid="7937603775306661863">"タップしてスキャンします"</string>
<string name="status_bar_work" msgid="5238641949837091056">"仕事用プロファイル"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"機内モード"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"次回のアラーム(<xliff:g id="WHEN">%1$s</xliff:g>)は鳴りません"</string>
@@ -792,7 +791,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="SONG_NAME">%1$s</xliff:g>(アーティスト名: <xliff:g id="ARTIST_NAME">%2$s</xliff:g>)を <xliff:g id="APP_LABEL">%3$s</xliff:g> で再生"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> を <xliff:g id="APP_LABEL">%2$s</xliff:g> で再生"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"元に戻す"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"<xliff:g id="DEVICENAME">%1$s</xliff:g>で再生するにはもっと近づけてください"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"<xliff:g id="DEVICENAME">%1$s</xliff:g>で再生しています"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"無効: アプリをご確認ください"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"見つかりませんでした"</string>
@@ -880,4 +880,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"ユーザーの選択"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"バックグラウンドで実行中のアプリ"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"停止"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ja/tiles_states_strings.xml b/packages/SystemUI/res/values-ja/tiles_states_strings.xml
index 9197aab790b0..55cbe8ba868a 100644
--- a/packages/SystemUI/res/values-ja/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ja/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"OFF"</item>
<item msgid="2075645297847971154">"ON"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"使用不可"</item>
+ <item msgid="1909756493418256167">"OFF"</item>
+ <item msgid="4531508423703413340">"ON"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"使用不可"</item>
<item msgid="9103697205127645916">"OFF"</item>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index 304c19d2af38..b932c452207a 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -233,8 +233,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi არ არის დაკავშირებული"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"განათება"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"ფერთა ინვერსია"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"ფერთა კორექცია"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"დამატებითი პარამეტრები"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"მომხმარებლის პარამეტრები"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"დასრულდა"</string>
@@ -450,8 +449,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"გამოსაყენებლად განბლოკვა"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"თქვენი ბარათების მიღებისას პრობლემა წარმოიშვა. ცადეთ ხელახლა მოგვიანებით"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"ჩაკეტილი ეკრანის პარამეტრები"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR-ის სკანირება"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"დააწკაპუნეთ QR კოდის სკანირებისთვის"</string>
+ <string name="qr_code_scanner_title" msgid="5660820608548306581">"QR კოდი"</string>
+ <string name="qr_code_scanner_description" msgid="7937603775306661863">"შეეხეთ დასასკანირებლად"</string>
<string name="status_bar_work" msgid="5238641949837091056">"სამსახურის პროფილი"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"თვითმფრინავის რეჟიმი"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"ვერ გაიგონებთ მომდევნო მაღვიძარას <xliff:g id="WHEN">%1$s</xliff:g>-ზე"</string>
@@ -792,7 +791,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"დაუკარით <xliff:g id="SONG_NAME">%1$s</xliff:g>, <xliff:g id="ARTIST_NAME">%2$s</xliff:g>, <xliff:g id="APP_LABEL">%3$s</xliff:g>-დან"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"დაუკარით <xliff:g id="SONG_NAME">%1$s</xliff:g> <xliff:g id="APP_LABEL">%2$s</xliff:g>-დან"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"მოქმედების გაუქმება"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"მიიტანეტ უფრო ახლოს, რომ დაუკრათ <xliff:g id="DEVICENAME">%1$s</xliff:g>-ზე"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"მიმდინარეობს დაკვრა <xliff:g id="DEVICENAME">%1$s</xliff:g>-ზე"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"არააქტიურია, გადაამოწმეთ აპი"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"ვერ მოიძებნა"</string>
@@ -880,4 +880,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"მომხმარებლის არჩევა"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"ფონურად მომუშავე აპები"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"შეწყვეტა"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ka/tiles_states_strings.xml b/packages/SystemUI/res/values-ka/tiles_states_strings.xml
index 485c3de7bdcf..34caeff0a9b9 100644
--- a/packages/SystemUI/res/values-ka/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ka/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"გამორთულია"</item>
<item msgid="2075645297847971154">"ჩართულია"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"მიუწვდომელია"</item>
+ <item msgid="1909756493418256167">"გამორთვა"</item>
+ <item msgid="4531508423703413340">"ჩართვა"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"მიუწვდომელია"</item>
<item msgid="9103697205127645916">"გამორთულია"</item>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index 90865f0169b4..1ac6c7ec66af 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -233,8 +233,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi желісіне жалғанбаған"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Жарықтығы"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Түс инверсиясы"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Түсті түзету"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Қосымша параметрлер"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Пайдаланушы параметрлері"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Дайын"</string>
@@ -450,8 +449,9 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Пайдалану үшін құлыпты ашу"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Карталарыңыз алынбады, кейінірек қайталап көріңіз."</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Экран құлпының параметрлері"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR кодын сканерлеу"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"QR кодын сканерлеу үшін басыңыз."</string>
+ <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+ <skip />
+ <string name="qr_code_scanner_description" msgid="7937603775306661863">"Сканерлеу үшін түртіңіз."</string>
<string name="status_bar_work" msgid="5238641949837091056">"Жұмыс профилі"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Ұшақ режимі"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Келесі <xliff:g id="WHEN">%1$s</xliff:g> дабылыңызды есітпейсіз"</string>
@@ -792,7 +792,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="APP_LABEL">%3$s</xliff:g> қолданбасында <xliff:g id="ARTIST_NAME">%2$s</xliff:g> орындайтын \"<xliff:g id="SONG_NAME">%1$s</xliff:g>\" әнін ойнату"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="APP_LABEL">%2$s</xliff:g> қолданбасында \"<xliff:g id="SONG_NAME">%1$s</xliff:g>\" әнін ойнату"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Қайтару"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"<xliff:g id="DEVICENAME">%1$s</xliff:g> құрылғысында музыка ойнату үшін оған жақындаңыз."</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"<xliff:g id="DEVICENAME">%1$s</xliff:g> құрылғысында ойнатылуда."</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Өшірулі. Қолданба тексеріңіз."</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Табылмады"</string>
@@ -880,4 +881,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Пайдаланушыны таңдау"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"Фондық режимде жұмыс істеп тұрған қолданбалар"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Тоқтату"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-kk/tiles_states_strings.xml b/packages/SystemUI/res/values-kk/tiles_states_strings.xml
index b143632803cb..616ad5362e63 100644
--- a/packages/SystemUI/res/values-kk/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-kk/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"Өшірулі"</item>
<item msgid="2075645297847971154">"Қосулы"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"Қолжетімді емес"</item>
+ <item msgid="1909756493418256167">"Өшірулі"</item>
+ <item msgid="4531508423703413340">"Қосулы"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"Қолжетімсіз"</item>
<item msgid="9103697205127645916">"Өшірулі"</item>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index 4b669998723b..bc69da90b510 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -233,8 +233,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"មិនមាន​ការតភ្ជាប់ Wi-Fi ទេ"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"ពន្លឺ"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"ការបញ្ច្រាស​ពណ៌"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"ការ​កែតម្រូវ​ពណ៌"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"ការ​កំណត់​ច្រើន​ទៀត"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"ការកំណត់អ្នកប្រើប្រាស់"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"រួចរាល់"</string>
@@ -450,8 +449,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"ដោះសោដើម្បីប្រើប្រាស់"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"មានបញ្ហា​ក្នុងការទាញយក​កាត​របស់អ្នក សូម​ព្យាយាមម្ដងទៀត​នៅពេលក្រោយ"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"ការកំណត់អេក្រង់ចាក់សោ"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"ស្កេនកូដ QR"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"ចុចដើម្បីស្កេនកូដ QR"</string>
+ <string name="qr_code_scanner_title" msgid="5660820608548306581">"​កូដ QR"</string>
+ <string name="qr_code_scanner_description" msgid="7937603775306661863">"ចុច​ដើម្បីស្កេន"</string>
<string name="status_bar_work" msgid="5238641949837091056">"ប្រវត្តិរូបការងារ"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"ពេលជិះយន្តហោះ"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"អ្នកនឹងមិនលឺម៉ោងរោទ៍ <xliff:g id="WHEN">%1$s</xliff:g> បន្ទាប់របស់អ្នកទេ"</string>
@@ -792,7 +791,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"ចាក់ <xliff:g id="SONG_NAME">%1$s</xliff:g> ច្រៀងដោយ <xliff:g id="ARTIST_NAME">%2$s</xliff:g> ពី <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"ចាក់ <xliff:g id="SONG_NAME">%1$s</xliff:g> ពី <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"ត្រឡប់វិញ"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"រំកិលឱ្យកាន់តែជិត ដើម្បីចាក់នៅលើ <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"កំពុង​ចាក់​​នៅ​លើ <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"អសកម្ម ពិនិត្យមើល​កម្មវិធី"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"រកមិន​ឃើញទេ"</string>
@@ -880,4 +880,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"ជ្រើសរើសអ្នកប្រើប្រាស់"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"កម្មវិធីដែលកំពុងដំណើរការ​នៅផ្ទៃខាងក្រោយ"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"ឈប់"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-km/tiles_states_strings.xml b/packages/SystemUI/res/values-km/tiles_states_strings.xml
index 38d3894d07e1..b1a1a8fbcd44 100644
--- a/packages/SystemUI/res/values-km/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-km/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"បិទ"</item>
<item msgid="2075645297847971154">"បើក"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"មិនអាចកែតម្រូវបានទេ"</item>
+ <item msgid="1909756493418256167">"បិទ"</item>
+ <item msgid="4531508423703413340">"បើក"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"មិនមានទេ"</item>
<item msgid="9103697205127645916">"បិទ"</item>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index bbb9ef1bbfc9..c388d82ab962 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -233,8 +233,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"ವೈ-ಫೈ ಸಂಪರ್ಕಗೊಂಡಿಲ್ಲ"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"ಪ್ರಕಾಶಮಾನ"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"ಕಲರ್ ಇನ್‍ವರ್ಶನ್"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"ಬಣ್ಣದ ತಿದ್ದುಪಡಿ"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"ಹೆಚ್ಚಿನ ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"ಬಳಕೆದಾರರ ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"ಮುಗಿದಿದೆ"</string>
@@ -450,8 +449,10 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"ಬಳಸಲು ಅನ್‌ಲಾಕ್ ಮಾಡಿ"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"ನಿಮ್ಮ ಕಾರ್ಡ್‌ಗಳನ್ನು ಪಡೆಯುವಾಗ ಸಮಸ್ಯೆ ಉಂಟಾಗಿದೆ, ನಂತರ ಪುನಃ ಪ್ರಯತ್ನಿಸಿ"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"ಲಾಕ್ ಸ್ಕ್ರ್ರೀನ್ ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR ಕೋಡ್ ಸ್ಕ್ಯಾನ್ ಮಾಡಿ"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"QR ಕೋಡ್ ಅನ್ನು ಸ್ಕ್ಯಾನ್ ಮಾಡಲು ಕ್ಲಿಕ್ ಮಾಡಿ"</string>
+ <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+ <skip />
+ <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+ <skip />
<string name="status_bar_work" msgid="5238641949837091056">"ಕೆಲಸದ ಪ್ರೊಫೈಲ್"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"ಏರ್‌ಪ್ಲೇನ್ ಮೋಡ್"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"ನಿಮ್ಮ ಮುಂದಿನ <xliff:g id="WHEN">%1$s</xliff:g> ಅಲಾರಮ್ ಅನ್ನು ನೀವು ಆಲಿಸುವುದಿಲ್ಲ"</string>
@@ -792,7 +793,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="ARTIST_NAME">%2$s</xliff:g> ಅವರ <xliff:g id="SONG_NAME">%1$s</xliff:g> ಹಾಡನ್ನು <xliff:g id="APP_LABEL">%3$s</xliff:g> ನಲ್ಲಿ ಪ್ಲೇ ಮಾಡಿ"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> ಹಾಡನ್ನು <xliff:g id="APP_LABEL">%2$s</xliff:g> ನಲ್ಲಿ ಪ್ಲೇ ಮಾಡಿ"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"ರದ್ದುಗೊಳಿಸಿ"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"<xliff:g id="DEVICENAME">%1$s</xliff:g> ನಲ್ಲಿ ಪ್ಲೇ ಮಾಡಲು ಅದರ ಹತ್ತಿರಕ್ಕೆ ಸರಿಯಿರಿ"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"<xliff:g id="DEVICENAME">%1$s</xliff:g> ನಲ್ಲಿ ಪ್ಲೇ ಆಗುತ್ತಿದೆ"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"ನಿಷ್ಕ್ರಿಯ, ಆ್ಯಪ್ ಪರಿಶೀಲಿಸಿ"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"ಕಂಡುಬಂದಿಲ್ಲ"</string>
@@ -880,4 +882,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"ಬಳಕೆದಾರ ಆಯ್ಕೆಮಾಡಿ"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"ಹಿನ್ನೆಲೆಯಲ್ಲಿ ರನ್ ಆಗುತ್ತಿರುವ ಆ್ಯಪ್‌ಗಳು"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"ನಿಲ್ಲಿಸಿ"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-kn/tiles_states_strings.xml b/packages/SystemUI/res/values-kn/tiles_states_strings.xml
index 022c5cf35f5c..e5bf6efc4edd 100644
--- a/packages/SystemUI/res/values-kn/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-kn/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"ಆಫ್ ಮಾಡಿ"</item>
<item msgid="2075645297847971154">"ಆನ್ ಮಾಡಿ"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"ಲಭ್ಯವಿಲ್ಲ"</item>
+ <item msgid="1909756493418256167">"ಆಫ್ ಮಾಡಿ"</item>
+ <item msgid="4531508423703413340">"ಆನ್ ಮಾಡಿ"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"ಲಭ್ಯವಿಲ್ಲ"</item>
<item msgid="9103697205127645916">"ಆಫ್ ಮಾಡಿ"</item>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 4275960b8cbc..16b5447df366 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -233,8 +233,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi가 연결되지 않음"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"밝기"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"색상 반전"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"색상 보정"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"설정 더보기"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"사용자 설정"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"완료"</string>
@@ -450,8 +449,10 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"잠금 해제하여 사용"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"카드를 가져오는 중에 문제가 발생했습니다. 나중에 다시 시도해 보세요."</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"잠금 화면 설정"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR 스캔"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"QR 코드를 스캔하려면 클릭하세요."</string>
+ <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+ <skip />
+ <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+ <skip />
<string name="status_bar_work" msgid="5238641949837091056">"직장 프로필"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"비행기 모드"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"<xliff:g id="WHEN">%1$s</xliff:g>에 다음 알람을 들을 수 없습니다."</string>
@@ -792,7 +793,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="APP_LABEL">%3$s</xliff:g>에서 <xliff:g id="ARTIST_NAME">%2$s</xliff:g>의 <xliff:g id="SONG_NAME">%1$s</xliff:g> 재생"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="APP_LABEL">%2$s</xliff:g>에서 <xliff:g id="SONG_NAME">%1$s</xliff:g> 재생"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"실행취소"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"<xliff:g id="DEVICENAME">%1$s</xliff:g>에서 재생하려면 기기를 더 가까이로 옮기세요."</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"<xliff:g id="DEVICENAME">%1$s</xliff:g>에서 재생 중"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"비활성. 앱을 확인하세요."</string>
<string name="controls_error_removed" msgid="6675638069846014366">"찾을 수 없음"</string>
@@ -880,4 +882,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"사용자 선택"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"백그라운드에서 실행 중인 앱"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"중지"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ko/tiles_states_strings.xml b/packages/SystemUI/res/values-ko/tiles_states_strings.xml
index ae6f148270c5..595b12c4baa0 100644
--- a/packages/SystemUI/res/values-ko/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ko/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"꺼짐"</item>
<item msgid="2075645297847971154">"켜짐"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"사용할 수 없음"</item>
+ <item msgid="1909756493418256167">"꺼짐"</item>
+ <item msgid="4531508423703413340">"켜짐"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"이용 불가"</item>
<item msgid="9103697205127645916">"꺼짐"</item>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index 448fb843ea2c..31c8522de2bc 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -233,8 +233,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi туташкан жок"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Жарыктыгы"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Түстү инверсиялоо"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Түсүн тууралоо"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Дагы жөндөөлөр"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Колдонуучунун жөндөөлөрү"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Бүттү"</string>
@@ -450,8 +449,9 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Колдонуу үчүн кулпусун ачыңыз"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Кыйытмаларды алууда ката кетти. Бир аздан кийин кайталап көрүңүз."</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Кулпуланган экран жөндөөлөрү"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR кодун скандоо"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"QR кодун скандоо үчүн чыкылдатыңыз"</string>
+ <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+ <skip />
+ <string name="qr_code_scanner_description" msgid="7937603775306661863">"Скандоо үчүн таптап коюңуз"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Жумуш профили"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Учак режими"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"<xliff:g id="WHEN">%1$s</xliff:g> боло турган кийинки эскертмени укпайсыз"</string>
@@ -792,7 +792,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="SONG_NAME">%1$s</xliff:g> ырын (аткаруучу: <xliff:g id="ARTIST_NAME">%2$s</xliff:g>) <xliff:g id="APP_LABEL">%3$s</xliff:g> колдонмосунан ойнотуу"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> ырын <xliff:g id="APP_LABEL">%2$s</xliff:g> колдонмосунан ойнотуу"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Кайтаруу"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"<xliff:g id="DEVICENAME">%1$s</xliff:g> түзмөгүндө ойнотуу үчүн жакыныраак жылдырыңыз"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"<xliff:g id="DEVICENAME">%1$s</xliff:g> аркылуу ойнотулууда"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Жигерсиз. Колдонмону текшериңиз"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Табылган жок"</string>
@@ -880,4 +881,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Колдонуучуну тандоо"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"Фондо иштеп жаткан колдонмолор"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Токтотуу"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ky/tiles_states_strings.xml b/packages/SystemUI/res/values-ky/tiles_states_strings.xml
index 0eadc34e37ba..3bcbf531d14a 100644
--- a/packages/SystemUI/res/values-ky/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ky/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"Өчүк"</item>
<item msgid="2075645297847971154">"Күйүк"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"Жеткиликсиз"</item>
+ <item msgid="1909756493418256167">"Өчүк"</item>
+ <item msgid="4531508423703413340">"Күйүк"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"Жеткиликсиз"</item>
<item msgid="9103697205127645916">"Өчүк"</item>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 702b76e1e7ec..f3884bc110ad 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -233,8 +233,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"ບໍ່ໄດ້ເຊື່ອມຕໍ່ Wi-Fi"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"ຄວາມແຈ້ງ"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"ການປີ້ນສີ"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"ການແກ້ໄຂສີ"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"​ການ​ຕັ້ງ​ຄ່າ​ເພີ່ມ​ເຕີມ"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"ຕັ້ງຄ່າຜູ້ໃຊ້"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"ແລ້ວໆ"</string>
@@ -450,8 +449,9 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"ປົດລັອກເພື່ອໃຊ້"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"ເກີດບັນຫາໃນການໂຫຼດບັດຂອງທ່ານ, ກະລຸນາລອງໃໝ່ໃນພາຍຫຼັງ"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"ການຕັ້ງຄ່າໜ້າຈໍລັອກ"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"ສະແກນ QR"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"ຄລິກເພື່ອສະແກນລະຫັດ QR"</string>
+ <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+ <skip />
+ <string name="qr_code_scanner_description" msgid="7937603775306661863">"ແຕະເພື່ອສະແກນ"</string>
<string name="status_bar_work" msgid="5238641949837091056">"​ໂປຣ​ໄຟລ໌​ບ່ອນ​ເຮັດ​ວຽກ"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"ໂໝດເຮືອ​ບິນ"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"ທ່ານ​ຈະ​ບໍ່​ໄດ້​ຍິນ​ສຽງ​ໂມງ​ປ <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -792,7 +792,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"ຫຼິ້ນ <xliff:g id="SONG_NAME">%1$s</xliff:g> ໂດຍ <xliff:g id="ARTIST_NAME">%2$s</xliff:g> ຈາກ <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"ຫຼິ້ນ <xliff:g id="SONG_NAME">%1$s</xliff:g> ຈາກ <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"ຍົກເລີກ"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"ຍ້າຍໄປໃກ້ຂຶ້ນເພື່ອຫຼິ້ນຢູ່ <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"ກຳລັງຫຼິ້ນຢູ່ <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"ບໍ່ເຮັດວຽກ, ກະລຸນາກວດສອບແອັບ"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"ບໍ່ພົບ"</string>
@@ -880,4 +881,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"ເລືອກຜູ້ໃຊ້"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"ແອັບທີ່ກຳລັງເອີ້ນໃຊ້ໃນພື້ນຫຼັງ"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"ຢຸດ"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-lo/tiles_states_strings.xml b/packages/SystemUI/res/values-lo/tiles_states_strings.xml
index 5fe5cfff03bf..0cb8afd2aca3 100644
--- a/packages/SystemUI/res/values-lo/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-lo/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"ປິດ"</item>
<item msgid="2075645297847971154">"ເປີດ"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"ບໍ່ສາມາດໃຊ້ໄດ້"</item>
+ <item msgid="1909756493418256167">"ປິດ"</item>
+ <item msgid="4531508423703413340">"ເປີດ"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"ບໍ່ສາມາດໃຊ້ໄດ້"</item>
<item msgid="9103697205127645916">"ປິດ"</item>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index eb593623d363..f2918d58e837 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -235,8 +235,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"„Wi-Fi“ neprijungtas"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Šviesumas"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Spalvų inversija"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Spalvų taisymas"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Daugiau nustatymų"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Naudotojo nustatymai"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Atlikta"</string>
@@ -456,8 +455,9 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Atrakinti, kad būtų galima naudoti"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Gaunant korteles kilo problema, bandykite dar kartą vėliau"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Užrakinimo ekrano nustatymai"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"Nuskaityti QR kodą"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"Spustelėkite, kad nuskaitytumėte QR kodą"</string>
+ <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+ <skip />
+ <string name="qr_code_scanner_description" msgid="7937603775306661863">"Palieskite, kad nuskaitytumėte"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Darbo profilis"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Lėktuvo režimas"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Negirdėsite kito signalo <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -804,7 +804,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Leisti <xliff:g id="ARTIST_NAME">%2$s</xliff:g> – „<xliff:g id="SONG_NAME">%1$s</xliff:g>“ iš „<xliff:g id="APP_LABEL">%3$s</xliff:g>“"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Leisti „<xliff:g id="SONG_NAME">%1$s</xliff:g>“ iš „<xliff:g id="APP_LABEL">%2$s</xliff:g>“"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Anuliuoti"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Prieikite arčiau, kad galėtumėte leisti įrenginyje „<xliff:g id="DEVICENAME">%1$s</xliff:g>“"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"Leidžiama įrenginyje „<xliff:g id="DEVICENAME">%1$s</xliff:g>“"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Neaktyvu, patikrinkite progr."</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Nerasta"</string>
@@ -892,4 +893,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Naudotojo pasirinkimas"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"Fone veikiančios programos"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Sustabdyti"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-lt/tiles_states_strings.xml b/packages/SystemUI/res/values-lt/tiles_states_strings.xml
index 7a0caa9c9afa..44a3fd52a6ef 100644
--- a/packages/SystemUI/res/values-lt/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-lt/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"Išjungta"</item>
<item msgid="2075645297847971154">"Įjungta"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"Nepasiekiama"</item>
+ <item msgid="1909756493418256167">"Išjungta"</item>
+ <item msgid="4531508423703413340">"Įjungta"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"Nepasiekiama"</item>
<item msgid="9103697205127645916">"Išjungta"</item>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index a27036e7728b..86a5df8ff901 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -234,8 +234,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Nav izveidots savienojums ar Wi-Fi"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Spilgtums"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Krāsu inversija"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Krāsu korekcija"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Vairāk iestatījumu"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Lietotāja iestatījumi"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Gatavs"</string>
@@ -453,8 +452,10 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Lai izmantotu, atbloķējiet ekrānu"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Ienesot jūsu kartes, radās problēma. Lūdzu, vēlāk mēģiniet vēlreiz."</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Bloķēšanas ekrāna iestatījumi"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"Ātrās atbildes koda skeneris"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"Noklikšķiniet, lai skenētu ātrās atbildes kodu."</string>
+ <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+ <skip />
+ <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+ <skip />
<string name="status_bar_work" msgid="5238641949837091056">"Darba profils"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Lidojuma režīms"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Nākamais signāls (<xliff:g id="WHEN">%1$s</xliff:g>) netiks atskaņots."</string>
@@ -798,7 +799,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Atskaņojiet failu “<xliff:g id="SONG_NAME">%1$s</xliff:g>” (izpildītājs: <xliff:g id="ARTIST_NAME">%2$s</xliff:g>) no lietotnes <xliff:g id="APP_LABEL">%3$s</xliff:g>."</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Atskaņojiet failu “<xliff:g id="SONG_NAME">%1$s</xliff:g>” no lietotnes <xliff:g id="APP_LABEL">%2$s</xliff:g>."</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Atsaukt"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Pārvietojiet savu ierīci tuvāk, lai atskaņotu mūziku ierīcē “<xliff:g id="DEVICENAME">%1$s</xliff:g>”"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"Notiek atskaņošana ierīcē “<xliff:g id="DEVICENAME">%1$s</xliff:g>”"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Neaktīva, pārbaudiet lietotni"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Netika atrasta"</string>
@@ -886,4 +888,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Lietotāja atlase"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"Lietotnes, kas darbojas fonā"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Apturēt"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-lv/tiles_states_strings.xml b/packages/SystemUI/res/values-lv/tiles_states_strings.xml
index 872dba60ca1d..35264ae2459d 100644
--- a/packages/SystemUI/res/values-lv/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-lv/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"Izslēgts"</item>
<item msgid="2075645297847971154">"Ieslēgts"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"Nav pieejama"</item>
+ <item msgid="1909756493418256167">"Izslēgta"</item>
+ <item msgid="4531508423703413340">"Ieslēgta"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"Nav pieejama"</item>
<item msgid="9103697205127645916">"Izslēgta"</item>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index 7f60c25f5bc7..b21f18375222 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -233,8 +233,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi не е поврзано"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Осветленост"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Инверзија на боите"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Корекција на боите"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Повеќе поставки"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Поставки на корисникот"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Готово"</string>
@@ -450,8 +449,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Отклучете за да користите"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Имаше проблем при преземањето на картичките. Обидете се повторно подоцна"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Поставки за заклучен екран"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"Скенирајте QR-код"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"Кликнете за да скенирате QR-код"</string>
+ <string name="qr_code_scanner_title" msgid="5660820608548306581">"QR-код"</string>
+ <string name="qr_code_scanner_description" msgid="7937603775306661863">"Допрете за скенирање"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Работен профил"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Авионски режим"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Нема да го слушнете следниот аларм <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -792,7 +791,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Пуштете <xliff:g id="SONG_NAME">%1$s</xliff:g> од <xliff:g id="ARTIST_NAME">%2$s</xliff:g> на <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Пуштете <xliff:g id="SONG_NAME">%1$s</xliff:g> на <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Врати"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Приближете се за да пуштите на <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"Се репродуцира на <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Неактивна, провери апликација"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Не е најдено"</string>
@@ -880,4 +880,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Изберете корисник"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"Апликации се извршуваат во заднина"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Крај"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-mk/tiles_states_strings.xml b/packages/SystemUI/res/values-mk/tiles_states_strings.xml
index 65e94f371e7f..c2c6f5dd1f23 100644
--- a/packages/SystemUI/res/values-mk/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-mk/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"Исклучено"</item>
<item msgid="2075645297847971154">"Вклучено"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"Недостапна"</item>
+ <item msgid="1909756493418256167">"Исклучена"</item>
+ <item msgid="4531508423703413340">"Вклучена"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"Недостапно"</item>
<item msgid="9103697205127645916">"Исклучено"</item>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index a65593cef66a..798ece02b4f7 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -233,8 +233,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"വൈഫൈ കണക്റ്റ് ചെയ്‌തിട്ടില്ല"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"തെളിച്ചം"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"നിറം വിപരീതമാക്കൽ"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"നിറം ശരിയാക്കൽ"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"കൂടുതൽ ക്രമീകരണങ്ങൾ"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"ഉപയോക്തൃ ക്രമീകരണം"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"പൂർത്തിയാക്കി"</string>
@@ -450,8 +449,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"ഉപയോഗിക്കാൻ അൺലോക്ക് ചെയ്യുക"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"നിങ്ങളുടെ കാർഡുകൾ ലഭ്യമാക്കുന്നതിൽ ഒരു പ്രശ്‌നമുണ്ടായി, പിന്നീട് വീണ്ടും ശ്രമിക്കുക"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"ലോക്ക് സ്ക്രീൻ ക്രമീകരണം"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR സ്‌കാൻ ചെയ്യുക"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"QR കോഡ് സ്‌കാൻ ചെയ്യാൻ ക്ലിക്ക് ചെയ്യുക"</string>
+ <string name="qr_code_scanner_title" msgid="5660820608548306581">"QR കോഡ്"</string>
+ <string name="qr_code_scanner_description" msgid="7937603775306661863">"സ്‌കാൻ ചെയ്യാൻ ടാപ്പ് ചെയ്യുക"</string>
<string name="status_bar_work" msgid="5238641949837091056">"ഔദ്യോഗിക പ്രൊഫൈൽ"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"ഫ്ലൈറ്റ് മോഡ്"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"<xliff:g id="WHEN">%1$s</xliff:g>-നുള്ള നിങ്ങളുടെ അടുത്ത അലാറം കേൾക്കില്ല"</string>
@@ -792,7 +791,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="ARTIST_NAME">%2$s</xliff:g> എന്ന ആർട്ടിസ്റ്റിന്റെ <xliff:g id="SONG_NAME">%1$s</xliff:g> എന്ന ഗാനം <xliff:g id="APP_LABEL">%3$s</xliff:g> ആപ്പിൽ പ്ലേ ചെയ്യുക"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> എന്ന ഗാനം <xliff:g id="APP_LABEL">%2$s</xliff:g> ആപ്പിൽ പ്ലേ ചെയ്യുക"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"പഴയപടിയാക്കുക"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"<xliff:g id="DEVICENAME">%1$s</xliff:g> എന്നതിൽ പ്ലേ ചെയ്യാൻ അടുത്തേക്ക് നീക്കുക"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"<xliff:g id="DEVICENAME">%1$s</xliff:g> എന്നതിൽ പ്ലേ ചെയ്യുന്നു"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"നിഷ്‌ക്രിയം, ആപ്പ് പരിശോധിക്കൂ"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"കണ്ടെത്തിയില്ല"</string>
@@ -880,4 +880,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"ഉപയോക്താവിനെ തിരഞ്ഞെടുക്കൂ"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"ആപ്പുകൾ പശ്ചാത്തലത്തിൽ റൺ ചെയ്യുന്നു"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"നിർത്തുക"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ml/tiles_states_strings.xml b/packages/SystemUI/res/values-ml/tiles_states_strings.xml
index 8746c74bd00a..c683c1b14c7d 100644
--- a/packages/SystemUI/res/values-ml/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ml/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"ഓഫാണ്"</item>
<item msgid="2075645297847971154">"ഓണാണ്"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"ലഭ്യമല്ല"</item>
+ <item msgid="1909756493418256167">"ഓഫാണ്"</item>
+ <item msgid="4531508423703413340">"ഓണാണ്"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"ലഭ്യമല്ല"</item>
<item msgid="9103697205127645916">"ഓഫാണ്"</item>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 9cc0b3f0628b..d283916dd0e8 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -233,8 +233,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi-д холбогдоогүй байна"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Тодрол"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Өнгө урвуулах"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Өнгөний засвар"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Бусад тохиргоо"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Хэрэглэгчийн тохиргоо"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Дууссан"</string>
@@ -450,8 +449,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Ашиглахын тулд түгжээг тайлах"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Таны картыг авахад асуудал гарлаа. Дараа дахин оролдоно уу"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Түгжигдсэн дэлгэцийн тохиргоо"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR-г скан хийх"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"QR кодыг скан хийхийн тулд товшино уу"</string>
+ <string name="qr_code_scanner_title" msgid="5660820608548306581">"QR код"</string>
+ <string name="qr_code_scanner_description" msgid="7937603775306661863">"Скан хийхийн тулд товшино уу"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Ажлын профайл"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Нислэгийн горим"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"<xliff:g id="WHEN">%1$s</xliff:g>-т та дараагийн сэрүүлгээ сонсохгүй"</string>
@@ -792,7 +791,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="ARTIST_NAME">%2$s</xliff:g>-н <xliff:g id="SONG_NAME">%1$s</xliff:g>-г <xliff:g id="APP_LABEL">%3$s</xliff:g> дээр тоглуулах"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g>-г <xliff:g id="APP_LABEL">%2$s</xliff:g> дээр тоглуулах"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Болих"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"<xliff:g id="DEVICENAME">%1$s</xliff:g> дээр тоглуулахын тулд төхөөрөмжөө ойртуулна уу"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"<xliff:g id="DEVICENAME">%1$s</xliff:g> дээр тоглуулж байна"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Идэвхгүй байна, аппыг шалгана уу"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Олдсонгүй"</string>
@@ -880,4 +880,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Хэрэглэгч сонгох"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"Ард ажиллаж байгаа аппууд"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Зогсоох"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-mn/tiles_states_strings.xml b/packages/SystemUI/res/values-mn/tiles_states_strings.xml
index 07dde9f76a98..7e01fbd139d3 100644
--- a/packages/SystemUI/res/values-mn/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-mn/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"Унтраалттай"</item>
<item msgid="2075645297847971154">"Асаалттай"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"Боломжгүй"</item>
+ <item msgid="1909756493418256167">"Унтраалттай"</item>
+ <item msgid="4531508423703413340">"Асаалттай"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"Боломжгүй"</item>
<item msgid="9103697205127645916">"Унтраалттай"</item>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 4d3258785b60..933a0e406518 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -233,8 +233,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"वाय-फाय नाही"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"चमक"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"कलर इन्व्हर्जन"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"रंग सुधारणा"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"अधिक सेटिंग्ज"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"वापरकर्ता सेटिंग्ज"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"पूर्ण झाले"</string>
@@ -450,8 +449,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"वापरण्यासाठी अनलॉक करा"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"तुमची कार्ड मिळवताना समस्या आली, कृपया नंतर पुन्हा प्रयत्न करा"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"लॉक स्क्रीन सेटिंग्ज"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR स्कॅन करा"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"QR कोड स्कॅन करण्यासाठी क्लिक करा"</string>
+ <string name="qr_code_scanner_title" msgid="5660820608548306581">"QR कोड"</string>
+ <string name="qr_code_scanner_description" msgid="7937603775306661863">"स्कॅन करण्यासाठी टॅप करा"</string>
<string name="status_bar_work" msgid="5238641949837091056">"कार्य प्रोफाईल"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"विमान मोड"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"तुम्ही तुमचा <xliff:g id="WHEN">%1$s</xliff:g> वाजता होणारा पुढील अलार्म ऐकणार नाही"</string>
@@ -792,7 +791,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="APP_LABEL">%3$s</xliff:g> मध्ये <xliff:g id="ARTIST_NAME">%2$s</xliff:g> चे <xliff:g id="SONG_NAME">%1$s</xliff:g> प्ले करा"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="APP_LABEL">%2$s</xliff:g> मध्ये <xliff:g id="SONG_NAME">%1$s</xliff:g> प्ले करा"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"पहिल्यासारखे करा"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"<xliff:g id="DEVICENAME">%1$s</xliff:g> वर प्ले करण्यासाठी जवळ जा"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"<xliff:g id="DEVICENAME">%1$s</xliff:g> वर प्ले केला जात आहे"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"निष्क्रिय, ॲप तपासा"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"आढळले नाही"</string>
@@ -880,4 +880,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"वापरकर्ता निवडा"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"ॲप्स बॅकग्राउंडमध्ये रन होत आहेत"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"थांबवा"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-mr/tiles_states_strings.xml b/packages/SystemUI/res/values-mr/tiles_states_strings.xml
index f0ca33356bb6..7fd88cceecc9 100644
--- a/packages/SystemUI/res/values-mr/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-mr/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"बंद आहे"</item>
<item msgid="2075645297847971154">"सुरू आहे"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"उपलब्ध नाही"</item>
+ <item msgid="1909756493418256167">"बंद आहे"</item>
+ <item msgid="4531508423703413340">"सुरू आहे"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"उपलब्ध नाही"</item>
<item msgid="9103697205127645916">"बंद आहे"</item>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 355580406b95..4cf476b5db32 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -233,8 +233,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi tidak disambungkan"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Kecerahan"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Penyongsangan warna"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Pembetulan warna"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Lagi tetapan"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Tetapan pengguna"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Selesai"</string>
@@ -450,8 +449,9 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Buka kunci untuk menggunakan"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Terdapat masalah sewaktu mendapatkan kad anda. Sila cuba sebentar lagi"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Tetapan skrin kunci"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"Imbas QR"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"Klik untuk mengimbas kod QR"</string>
+ <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+ <skip />
+ <string name="qr_code_scanner_description" msgid="7937603775306661863">"Ketik untuk membuat imbasan"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Profil kerja"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Mod pesawat"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Anda tidak akan mendengar penggera yang seterusnya <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -792,7 +792,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Mainkan <xliff:g id="SONG_NAME">%1$s</xliff:g> oleh <xliff:g id="ARTIST_NAME">%2$s</xliff:g> daripada <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Mainkan <xliff:g id="SONG_NAME">%1$s</xliff:g> daripada <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Buat asal"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Alihkan lebih dekat untuk bermain pada<xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"Dimainkan pada <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Tidak aktif, semak apl"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Tidak ditemukan"</string>
@@ -880,4 +881,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Pilih pengguna"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"Apl berjalan di latar"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Berhenti"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ms/tiles_states_strings.xml b/packages/SystemUI/res/values-ms/tiles_states_strings.xml
index b682df1ca324..eaafd192506c 100644
--- a/packages/SystemUI/res/values-ms/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ms/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"Mati"</item>
<item msgid="2075645297847971154">"Hidup"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"Tidak tersedia"</item>
+ <item msgid="1909756493418256167">"Mati"</item>
+ <item msgid="4531508423703413340">"Hidup"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"Tidak tersedia"</item>
<item msgid="9103697205127645916">"Mati"</item>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index 26333edb77b1..8964ed529cb5 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -233,8 +233,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi ချိတ်ဆက်ထားခြင်းမရှိပါ"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"အလင်းတောက်ပမှု"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"အရောင်ပြောင်းပြန်ပြုလုပ်ရန်"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"အရောင် အမှန်ပြင်ခြင်း"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"နောက်ထပ် ဆက်တင်များ"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"အသုံးပြုသူ ဆက်တင်များ"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"ပြီးပါပြီ"</string>
@@ -450,8 +449,10 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"သုံးရန် လော့ခ်ဖွင့်ပါ"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"သင်၏ကတ်များ ရယူရာတွင် ပြဿနာရှိနေသည်၊ နောက်မှ ထပ်စမ်းကြည့်ပါ"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"လော့ခ်မျက်နှာပြင် ဆက်တင်များ"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR စကင်ဖတ်ခြင်း"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"QR ကုဒ် စကင်ဖတ်ရန် ကလစ်နှိပ်ပါ"</string>
+ <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+ <skip />
+ <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+ <skip />
<string name="status_bar_work" msgid="5238641949837091056">"အလုပ် ပရိုဖိုင်"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"လေယာဉ်ပျံမုဒ်"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"<xliff:g id="WHEN">%1$s</xliff:g> ၌သင့်နောက်ထပ် နှိုးစက်ကို ကြားမည်မဟုတ်ပါ"</string>
@@ -792,7 +793,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="ARTIST_NAME">%2$s</xliff:g> ၏ <xliff:g id="SONG_NAME">%1$s</xliff:g> ကို <xliff:g id="APP_LABEL">%3$s</xliff:g> တွင် ဖွင့်ပါ"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> ကို <xliff:g id="APP_LABEL">%2$s</xliff:g> တွင် ဖွင့်ပါ"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"နောက်ပြန်ရန်"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"<xliff:g id="DEVICENAME">%1$s</xliff:g> တွင်ဖွင့်ရန် အနီးသို့ရွှေ့ပါ"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"<xliff:g id="DEVICENAME">%1$s</xliff:g> တွင်ဖွင့်ထားသည်"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"ရပ်နေသည်၊ အက်ပ်ကို စစ်ဆေးပါ"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"မတွေ့ပါ"</string>
@@ -880,4 +882,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"အသုံးပြုသူ ရွေးခြင်း"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"နောက်ခံတွင် ဖွင့်ထားသောအက်ပ်များ"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"ရပ်ရန်"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-my/tiles_states_strings.xml b/packages/SystemUI/res/values-my/tiles_states_strings.xml
index af8d55c8cd7f..dfc8ccca736f 100644
--- a/packages/SystemUI/res/values-my/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-my/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"ပိတ်"</item>
<item msgid="2075645297847971154">"ဖွင့်"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"မရနိုင်ပါ"</item>
+ <item msgid="1909756493418256167">"ပိတ်"</item>
+ <item msgid="4531508423703413340">"ဖွင့်"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"မရနိုင်ပါ"</item>
<item msgid="9103697205127645916">"ပိတ်"</item>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 0102801191bf..6e66156a87ef 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -233,8 +233,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi er ikke tilkoblet"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Lysstyrke"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Fargeinvertering"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Fargekorrigering"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Flere innstillinger"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Brukerinnstillinger"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Ferdig"</string>
@@ -450,8 +449,10 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Lås opp for å bruke"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Det oppsto et problem med henting av kortene. Prøv igjen senere"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Innstillinger for låseskjermen"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"Skann QR-kode"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"Klikk for å skanne en QR-kode"</string>
+ <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+ <skip />
+ <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+ <skip />
<string name="status_bar_work" msgid="5238641949837091056">"Work-profil"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Flymodus"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Du hører ikke neste innstilte alarm <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -792,7 +793,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Spill av <xliff:g id="SONG_NAME">%1$s</xliff:g> av <xliff:g id="ARTIST_NAME">%2$s</xliff:g> fra <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Spill av <xliff:g id="SONG_NAME">%1$s</xliff:g> fra <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Angre"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Flytt nærmere for å spille av på <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"Spilles av på <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Inaktiv. Sjekk appen"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Ikke funnet"</string>
@@ -880,4 +882,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Velg bruker"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"Apper som kjører i bakgrunnen"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Stopp"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-nb/tiles_states_strings.xml b/packages/SystemUI/res/values-nb/tiles_states_strings.xml
index 619f6135d56f..38e10456d612 100644
--- a/packages/SystemUI/res/values-nb/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-nb/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"Av"</item>
<item msgid="2075645297847971154">"På"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"Utilgjengelig"</item>
+ <item msgid="1909756493418256167">"Av"</item>
+ <item msgid="4531508423703413340">"På"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"Utilgjengelig"</item>
<item msgid="9103697205127645916">"Av"</item>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index dc220c7ccf77..db64a7e4879f 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -233,8 +233,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi जडान गरिएको छैन"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"उज्यालपन"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"कलर इन्भर्सन"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"रङ सच्याउने कार्य"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"थप सेटिङहरू"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"प्रयोगकर्तासम्बन्धी सेटिङ"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"भयो"</string>
@@ -450,8 +449,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"यो वालेट प्रयोग गर्न डिभाइस अनलक गर्नुहोस्"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"तपाईंका कार्डहरू प्राप्त गर्ने क्रममा समस्या भयो, कृपया पछि फेरि प्रयास गर्नुहोस्"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"लक स्क्रिनसम्बन्धी सेटिङ"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR स्क्यान गर्नुहोस्"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"QR कोड स्क्यान गर्न क्लिक गर्नुहोस्"</string>
+ <string name="qr_code_scanner_title" msgid="5660820608548306581">"QR कोड"</string>
+ <string name="qr_code_scanner_description" msgid="7937603775306661863">"स्क्यान गर्न ट्याप गर्नुहोस्"</string>
<string name="status_bar_work" msgid="5238641949837091056">"कार्य प्रोफाइल"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"हवाइजहाज मोड"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"तपाईँले आफ्नो अर्को अलार्म <xliff:g id="WHEN">%1$s</xliff:g> सुन्नुहुने छैन"</string>
@@ -792,7 +791,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="ARTIST_NAME">%2$s</xliff:g> को <xliff:g id="SONG_NAME">%1$s</xliff:g> बोलको गीत <xliff:g id="APP_LABEL">%3$s</xliff:g> मा बजाउनुहोस्"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> बोलको गीत <xliff:g id="APP_LABEL">%2$s</xliff:g> मा बजाउनुहोस्"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"अन्डू गर्नुहोस्"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"<xliff:g id="DEVICENAME">%1$s</xliff:g> मा प्ले गर्न आफ्नो डिभाइस नजिकै लैजानुहोस्"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"<xliff:g id="DEVICENAME">%1$s</xliff:g> मा प्ले गरिँदै छ"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"निष्क्रिय छ, एप जाँच गर्नु…"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"फेला परेन"</string>
@@ -880,4 +880,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"प्रयोगकर्ता चयन गर्नु…"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"अहिले ब्याकग्राउन्डमा चलिरहेका एप"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"रोक्नुहोस्"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ne/tiles_states_strings.xml b/packages/SystemUI/res/values-ne/tiles_states_strings.xml
index 808b58d31ea1..abe94e763481 100644
--- a/packages/SystemUI/res/values-ne/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ne/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"अफ छ"</item>
<item msgid="2075645297847971154">"अन छ"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"उपलब्ध छैन"</item>
+ <item msgid="1909756493418256167">"अफ छ"</item>
+ <item msgid="4531508423703413340">"अन छ"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"उपलब्ध छैन"</item>
<item msgid="9103697205127645916">"अफ छ"</item>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index e7dbd2ecb019..b895d9ebd542 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -233,8 +233,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wifi niet verbonden"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Helderheid"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Kleurinversie"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Kleurcorrectie"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Meer instellingen"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Gebruikersinstellingen"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Klaar"</string>
@@ -450,8 +449,9 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Ontgrendelen om te gebruiken"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Er is een probleem opgetreden bij het ophalen van je kaarten. Probeer het later opnieuw."</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Instellingen voor vergrendelscherm"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR-code scannen"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"Klik om een QR-code te scannen"</string>
+ <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+ <skip />
+ <string name="qr_code_scanner_description" msgid="7937603775306661863">"Tik om te scannen"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Werkprofiel"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Vliegtuigmodus"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Je hoort je volgende wekker niet <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -792,7 +792,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="SONG_NAME">%1$s</xliff:g> van <xliff:g id="ARTIST_NAME">%2$s</xliff:g> afspelen via <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> afspelen via <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Ongedaan maken"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Ga dichter naar <xliff:g id="DEVICENAME">%1$s</xliff:g> toe om af te spelen"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"Afspelen op <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Inactief, check de app"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Niet gevonden"</string>
@@ -880,4 +881,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Gebruiker selecteren"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"Apps die op de achtergrond worden uitgevoerd"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Stoppen"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-nl/tiles_states_strings.xml b/packages/SystemUI/res/values-nl/tiles_states_strings.xml
index 92332ca81f4a..ac85f28daa37 100644
--- a/packages/SystemUI/res/values-nl/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-nl/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"Uit"</item>
<item msgid="2075645297847971154">"Aan"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"Niet beschikbaar"</item>
+ <item msgid="1909756493418256167">"Uit"</item>
+ <item msgid="4531508423703413340">"Aan"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"Niet beschikbaar"</item>
<item msgid="9103697205127645916">"Uit"</item>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index 334b7565be67..e47513c3e8f8 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -233,8 +233,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"ୱାଇ-ଫାଇ ସଂଯୋଜିତ ହୋଇନାହିଁ"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"ଉଜ୍ଜ୍ୱଳତା"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"ରଙ୍ଗ ଇନଭାର୍ସନ"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"ରଙ୍ଗ ସଂଶୋଧନ"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"ଅଧିକ ସେଟିଂସ୍"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"ଉପଯୋଗକର୍ତ୍ତା ସେଟିଂସ୍"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"ହୋଇଗଲା"</string>
@@ -450,8 +449,9 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"ବ୍ୟବହାର କରିବାକୁ ଅନଲକ୍ କରନ୍ତୁ"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"ଆପଣଙ୍କ କାର୍ଡଗୁଡ଼ିକ ପାଇବାରେ ଏକ ସମସ୍ୟା ହୋଇଥିଲା। ଦୟାକରି ପରେ ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"ସ୍କ୍ରିନ୍ ଲକ୍ ସେଟିଂସ୍"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR ସ୍କାନ କରନ୍ତୁ"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"ଏକ QR କୋଡ ସ୍କାନ କରିବାକୁ କ୍ଲିକ କରନ୍ତୁ"</string>
+ <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+ <skip />
+ <string name="qr_code_scanner_description" msgid="7937603775306661863">"ସ୍କାନ କରିବାକୁ ଟାପ କରନ୍ତୁ"</string>
<string name="status_bar_work" msgid="5238641949837091056">"ୱର୍କ ପ୍ରୋଫାଇଲ୍‌"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"ଏରୋପ୍ଲେନ୍‍ ମୋଡ୍"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"<xliff:g id="WHEN">%1$s</xliff:g>ବେଳେ ଆପଣ ନିଜର ପରବର୍ତ୍ତୀ ଆଲାର୍ମ ଶୁଣିପାରିବେ ନାହିଁ"</string>
@@ -792,7 +792,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="APP_LABEL">%3$s</xliff:g>ରୁ <xliff:g id="ARTIST_NAME">%2$s</xliff:g>ଙ୍କ <xliff:g id="SONG_NAME">%1$s</xliff:g> ଚଲାନ୍ତୁ"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="APP_LABEL">%2$s</xliff:g>ରୁ <xliff:g id="SONG_NAME">%1$s</xliff:g> ଚଲାନ୍ତୁ"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"ପୂର୍ବବତ୍ କରନ୍ତୁ"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"<xliff:g id="DEVICENAME">%1$s</xliff:g>ରେ ଚଲାଇବା ପାଇଁ ନିକଟକୁ ଯାଆନ୍ତୁ"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"<xliff:g id="DEVICENAME">%1$s</xliff:g>ରେ ଚାଲୁଛି"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"ନିଷ୍କ୍ରିୟ ଅଛି, ଆପ ଯାଞ୍ଚ କରନ୍ତୁ"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"ମିଳିଲା ନାହିଁ"</string>
@@ -880,4 +881,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"ଉପଯୋଗକର୍ତ୍ତା ଚୟନ କର"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"ପୃଷ୍ଠପଟରେ ଚାଲୁଥିବା ଆପଗୁଡ଼ିକ"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"ବନ୍ଦ କରନ୍ତୁ"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-or/tiles_states_strings.xml b/packages/SystemUI/res/values-or/tiles_states_strings.xml
index 94b012297f49..48ebb63e8ad2 100644
--- a/packages/SystemUI/res/values-or/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-or/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"ବନ୍ଦ ଅଛି"</item>
<item msgid="2075645297847971154">"ଚାଲୁ ଅଛି"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"ଉପଲବ୍ଧ ନାହିଁ"</item>
+ <item msgid="1909756493418256167">"ବନ୍ଦ ଅଛି"</item>
+ <item msgid="4531508423703413340">"ଚାଲୁ ଅଛି"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"ଉପଲବ୍ଧ ନାହିଁ"</item>
<item msgid="9103697205127645916">"ବନ୍ଦ ଅଛି"</item>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index 256ed366a087..3eae70aa7b8c 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -233,8 +233,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"ਵਾਈ-ਫਾਈ ਕਨੈਕਟ ਨਹੀਂ ਕੀਤਾ ਗਿਆ"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"ਚਮਕ"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"ਰੰਗ ਪਲਟਨਾ"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"ਰੰਗ ਸੁਧਾਈ"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"ਹੋਰ ਸੈਟਿੰਗਾਂ"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"ਵਰਤੋਂਕਾਰ ਸੈਟਿੰਗਾਂ"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"ਹੋ ਗਿਆ"</string>
@@ -450,8 +449,9 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"ਵਰਤਣ ਲਈ ਅਣਲਾਕ ਕਰੋ"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"ਤੁਹਾਡੇ ਕਾਰਡ ਪ੍ਰਾਪਤ ਕਰਨ ਵਿੱਚ ਕੋਈ ਸਮੱਸਿਆ ਆਈ, ਕਿਰਪਾ ਕਰਕੇ ਬਾਅਦ ਵਿੱਚ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"ਲਾਕ ਸਕ੍ਰੀਨ ਸੈਟਿੰਗਾਂ"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR ਸਕੈਨ ਕਰੋ"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"QR ਕੋਡ ਨੂੰ ਸਕੈਨ ਕਰਨ ਲਈ ਕਲਿੱਕ ਕਰੋ"</string>
+ <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+ <skip />
+ <string name="qr_code_scanner_description" msgid="7937603775306661863">"ਸਕੈਨ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
<string name="status_bar_work" msgid="5238641949837091056">"ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"ਹਵਾਈ-ਜਹਾਜ਼ ਮੋਡ"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"ਤੁਸੀਂ <xliff:g id="WHEN">%1$s</xliff:g> ਵਜੇ ਆਪਣਾ ਅਗਲਾ ਅਲਾਰਮ ਨਹੀਂ ਸੁਣੋਗੇ"</string>
@@ -792,7 +792,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="APP_LABEL">%3$s</xliff:g> ਤੋਂ <xliff:g id="ARTIST_NAME">%2$s</xliff:g> ਦਾ <xliff:g id="SONG_NAME">%1$s</xliff:g> ਚਲਾਓ"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="APP_LABEL">%2$s</xliff:g> ਤੋਂ <xliff:g id="SONG_NAME">%1$s</xliff:g> ਚਲਾਓ"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"ਅਣਕੀਤਾ ਕਰੋ"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"<xliff:g id="DEVICENAME">%1$s</xliff:g> \'ਤੇ ਚਲਾਉਣ ਲਈ ਨੇੜੇ ਲਿਜਾਓ"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"<xliff:g id="DEVICENAME">%1$s</xliff:g> \'ਤੇ ਚਲਾਇਆ ਜਾ ਰਿਹਾ ਹੈ"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"ਅਕਿਰਿਆਸ਼ੀਲ, ਐਪ ਦੀ ਜਾਂਚ ਕਰੋ"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"ਨਹੀਂ ਮਿਲਿਆ"</string>
@@ -880,4 +881,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"ਵਰਤੋਂਕਾਰ ਚੁਣੋ"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"ਬੈਕਗ੍ਰਾਊਂਡ ਵਿੱਚ ਚੱਲ ਰਹੀਆਂ ਐਪਾਂ"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"ਬੰਦ ਕਰੋ"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-pa/tiles_states_strings.xml b/packages/SystemUI/res/values-pa/tiles_states_strings.xml
index a7fc06626636..85c5d89eb5a2 100644
--- a/packages/SystemUI/res/values-pa/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-pa/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"ਬੰਦ ਹੈ"</item>
<item msgid="2075645297847971154">"ਚਾਲੂ ਹੈ"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"ਉਪਲਬਧ ਨਹੀਂ"</item>
+ <item msgid="1909756493418256167">"ਬੰਦ ਹੈ"</item>
+ <item msgid="4531508423703413340">"ਚਾਲੂ ਹੈ"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"ਅਣਉਪਲਬਧ ਹੈ"</item>
<item msgid="9103697205127645916">"ਬੰਦ ਹੈ"</item>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 8f57f0e59419..41ad75a3e64b 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -235,8 +235,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Brak połączenia z Wi-Fi"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Jasność"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Odwrócenie kolorów"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Korekcja kolorów"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Więcej ustawień"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Ustawienia użytkownika"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Gotowe"</string>
@@ -456,8 +455,10 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Odblokuj, aby użyć"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Podczas pobierania kart wystąpił problem. Spróbuj ponownie później."</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Ustawienia ekranu blokady"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"Skanowanie kodu QR"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"Kliknij, aby zeskanować kod QR"</string>
+ <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+ <skip />
+ <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+ <skip />
<string name="status_bar_work" msgid="5238641949837091056">"Profil służbowy"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Tryb samolotowy"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Nie usłyszysz swojego następnego alarmu <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -804,7 +805,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Odtwórz utwór <xliff:g id="SONG_NAME">%1$s</xliff:g> (<xliff:g id="ARTIST_NAME">%2$s</xliff:g>) w aplikacji <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Odtwórz utwór <xliff:g id="SONG_NAME">%1$s</xliff:g> w aplikacji <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Cofnij"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Przysuń się bliżej, aby odtwarzać na urządzeniu <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"Odtwarzam na urządzeniu <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Nieaktywny, sprawdź aplikację"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Nie znaleziono"</string>
@@ -892,4 +894,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Wybierz użytkownika"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"Aplikacje działające w tle"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Zatrzymaj"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-pl/tiles_states_strings.xml b/packages/SystemUI/res/values-pl/tiles_states_strings.xml
index 94fa858a0abb..8b922e53c18f 100644
--- a/packages/SystemUI/res/values-pl/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-pl/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"Wyłączony"</item>
<item msgid="2075645297847971154">"Włączony"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"Brak dostępu"</item>
+ <item msgid="1909756493418256167">"Wyłączono"</item>
+ <item msgid="4531508423703413340">"Włączono"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"Niedostępny"</item>
<item msgid="9103697205127645916">"Wyłączony"</item>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 3b37834f22e4..d21ea5498b4a 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -449,8 +449,9 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Desbloquear para usar"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Ocorreu um problema ao carregar os cards. Tente novamente mais tarde"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Configurações de tela de bloqueio"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"Ler código QR"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"Clique para ler um código QR"</string>
+ <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+ <skip />
+ <string name="qr_code_scanner_description" msgid="7937603775306661863">"Toque para fazer a leitura"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Perfil de trabalho"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Modo avião"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Você não ouvirá o próximo alarme às <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -791,7 +792,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Tocar <xliff:g id="SONG_NAME">%1$s</xliff:g> de <xliff:g id="ARTIST_NAME">%2$s</xliff:g> no app <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Tocar <xliff:g id="SONG_NAME">%1$s</xliff:g> no app <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Desfazer"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Aproxime os dispositivos para ouvir música no <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"Reproduzido em <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Inativo, verifique o app"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Não encontrado"</string>
@@ -879,4 +881,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Selecionar usuário"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"Apps em execução em segundo plano"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Parar"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 1195413fd280..556e92a9ff52 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -449,8 +449,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Desbloquear para utilizar"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Ocorreu um problema ao obter os seus cartões. Tente novamente mais tarde."</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Definições do ecrã de bloqueio"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"Leia o QR"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"Clique para ler um código QR"</string>
+ <string name="qr_code_scanner_title" msgid="5660820608548306581">"Código QR"</string>
+ <string name="qr_code_scanner_description" msgid="7937603775306661863">"Toque para ler"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Perfil de trabalho"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Modo de avião"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Não vai ouvir o próximo alarme às <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -791,7 +791,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Reproduzir <xliff:g id="SONG_NAME">%1$s</xliff:g> de <xliff:g id="ARTIST_NAME">%2$s</xliff:g> a partir da app <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Reproduzir <xliff:g id="SONG_NAME">%1$s</xliff:g> a partir da app <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Anular"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Aproxime-se para reproduzir no dispositivo <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"A reproduzir no dispositivo <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Inativa. Consulte a app."</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Não encontrado."</string>
@@ -879,4 +880,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Selecione utilizador"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"Apps em execução em segundo plano"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Parar"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 3b37834f22e4..d21ea5498b4a 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -449,8 +449,9 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Desbloquear para usar"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Ocorreu um problema ao carregar os cards. Tente novamente mais tarde"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Configurações de tela de bloqueio"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"Ler código QR"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"Clique para ler um código QR"</string>
+ <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+ <skip />
+ <string name="qr_code_scanner_description" msgid="7937603775306661863">"Toque para fazer a leitura"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Perfil de trabalho"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Modo avião"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Você não ouvirá o próximo alarme às <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -791,7 +792,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Tocar <xliff:g id="SONG_NAME">%1$s</xliff:g> de <xliff:g id="ARTIST_NAME">%2$s</xliff:g> no app <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Tocar <xliff:g id="SONG_NAME">%1$s</xliff:g> no app <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Desfazer"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Aproxime os dispositivos para ouvir música no <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"Reproduzido em <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Inativo, verifique o app"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Não encontrado"</string>
@@ -879,4 +881,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Selecionar usuário"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"Apps em execução em segundo plano"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Parar"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index f8023cf19c3d..ab3c186dbe5b 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -234,8 +234,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Rețeaua Wi-Fi nu este conectată"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Luminozitate"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inversarea culorilor"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Corecția culorii"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Mai multe setări"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Setări de utilizator"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Terminat"</string>
@@ -453,8 +452,9 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Deblocați pentru a folosi"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"A apărut o problemă la preluarea cardurilor. Încercați din nou mai târziu"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Setările ecranului de blocare"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"Scanați un cod QR"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"Dați clic pentru a scana un cod QR"</string>
+ <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+ <skip />
+ <string name="qr_code_scanner_description" msgid="7937603775306661863">"Atingeți pentru a scana"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Profil de serviciu"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Mod Avion"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Nu veți auzi următoarea alarmă <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -798,7 +798,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Redați <xliff:g id="SONG_NAME">%1$s</xliff:g> de la <xliff:g id="ARTIST_NAME">%2$s</xliff:g> în <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Redați <xliff:g id="SONG_NAME">%1$s</xliff:g> în <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Anulați"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Apropiați-vă pentru a reda pe <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"Se redă pe <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Inactiv, verificați aplicația"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Nu s-a găsit"</string>
@@ -886,4 +887,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Alegeți utilizatorul"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"Aplicațiile rulează în fundal"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Opriți"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ro/tiles_states_strings.xml b/packages/SystemUI/res/values-ro/tiles_states_strings.xml
index 708c6f03a1d7..ba936966550e 100644
--- a/packages/SystemUI/res/values-ro/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ro/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"Dezactivat"</item>
<item msgid="2075645297847971154">"Activat"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"Indisponibilă"</item>
+ <item msgid="1909756493418256167">"Dezactivată"</item>
+ <item msgid="4531508423703413340">"Activată"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"Indisponibilă"</item>
<item msgid="9103697205127645916">"Dezactivată"</item>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 9f40eab5e532..e92364c943d9 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -235,8 +235,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Нет подключения к сети Wi-Fi."</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Яркость"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Инверсия цветов"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Коррекция цвета"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Настройки"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Пользовательские настройки"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Готово"</string>
@@ -456,8 +455,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Разблокировать для использования"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Не удалось получить информацию о картах. Повторите попытку позже."</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Настройки заблокированного экрана"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"Сканер QR-кодов"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"Нажмите, чтобы отсканировать QR-код."</string>
+ <string name="qr_code_scanner_title" msgid="5660820608548306581">"QR-код"</string>
+ <string name="qr_code_scanner_description" msgid="7937603775306661863">"Нажмите, чтобы отсканировать код"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Рабочий профиль"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Режим полета"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Следующий будильник: <xliff:g id="WHEN">%1$s</xliff:g>. Звук отключен."</string>
@@ -804,7 +803,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Воспроизвести медиафайл \"<xliff:g id="SONG_NAME">%1$s</xliff:g>\" (исполнитель: <xliff:g id="ARTIST_NAME">%2$s</xliff:g>) из приложения \"<xliff:g id="APP_LABEL">%3$s</xliff:g>\""</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Воспроизвести медиафайл \"<xliff:g id="SONG_NAME">%1$s</xliff:g>\" из приложения \"<xliff:g id="APP_LABEL">%2$s</xliff:g>\""</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Отменить"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Чтобы включить музыку на устройстве \"<xliff:g id="DEVICENAME">%1$s</xliff:g>\", подойдите к нему ближе."</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"Воспроизводится на устройстве \"<xliff:g id="DEVICENAME">%1$s</xliff:g>\"."</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Нет ответа. Проверьте приложение."</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Не найдено."</string>
@@ -892,4 +892,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Выберите профиль"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"Приложения, работающие в фоновом режиме"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Остановить"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ru/tiles_states_strings.xml b/packages/SystemUI/res/values-ru/tiles_states_strings.xml
index 3a51c2e1b2b0..32e6ac978c77 100644
--- a/packages/SystemUI/res/values-ru/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ru/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"Откл."</item>
<item msgid="2075645297847971154">"Вкл."</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"Недоступно"</item>
+ <item msgid="1909756493418256167">"Отключено"</item>
+ <item msgid="4531508423703413340">"Включено"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"Функция недоступна"</item>
<item msgid="9103697205127645916">"Откл."</item>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index ce5b12690ff3..327025c488ff 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -233,8 +233,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi සම්බන්ධ නොවීය"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"දීප්තිමත් බව"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"වර්ණ අපවර්තනය"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"වර්ණ නිවැරදි කිරීම"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"තව සැකසීම්"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"පරිශීලක සැකසීම්"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"නිමයි"</string>
@@ -450,8 +449,10 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"භාවිත කිරීමට අගුලු හරින්න"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"ඔබගේ කාඩ්පත ලබා ගැනීමේ ගැටලුවක් විය, කරුණාකර පසුව නැවත උත්සාහ කරන්න"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"අගුලු තිර සැකසීම්"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR කේතය ස්කෑන් කරන්න"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"QR කේතයක් ස්කෑන් කිරීමට ක්ලික් කරන්න"</string>
+ <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+ <skip />
+ <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+ <skip />
<string name="status_bar_work" msgid="5238641949837091056">"කාර්යාල පැතිකඩ"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"ගුවන්යානා ප්‍රකාරය"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"ඔබට ඔබේ ඊළඟ එලාමය <xliff:g id="WHEN">%1$s</xliff:g> නොඇසෙනු ඇත"</string>
@@ -792,7 +793,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="ARTIST_NAME">%2$s</xliff:g>ගේ <xliff:g id="SONG_NAME">%1$s</xliff:g> <xliff:g id="APP_LABEL">%3$s</xliff:g> වෙතින් වාදනය කරන්න"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> <xliff:g id="APP_LABEL">%2$s</xliff:g> වෙතින් වාදනය කරන්න"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"පසුගමනය කරන්න"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"<xliff:g id="DEVICENAME">%1$s</xliff:g> හි වාදනය කිරීමට වඩාත් ළං වන්න"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"<xliff:g id="DEVICENAME">%1$s</xliff:g> හි වාදනය කරමින්"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"අක්‍රියයි, යෙදුම පරීක්ෂා කරන්න"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"හමු නොවිණි"</string>
@@ -880,4 +882,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"පරිශීලක තෝරන්න"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"පසුබිමින් ධාවනය වෙමින් පවතින යෙදුම්"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"නවත්වන්න"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-si/tiles_states_strings.xml b/packages/SystemUI/res/values-si/tiles_states_strings.xml
index 909d119f2c09..8929a3c5709d 100644
--- a/packages/SystemUI/res/values-si/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-si/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"අක්‍රියයි"</item>
<item msgid="2075645297847971154">"සක්‍රියයි"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"නොමැත"</item>
+ <item msgid="1909756493418256167">"ක්‍රියාවිරහිතයි"</item>
+ <item msgid="4531508423703413340">"ක්‍රියාත්මකයි"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"නොමැත"</item>
<item msgid="9103697205127645916">"අක්‍රියයි"</item>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 209b031bc484..bb8870fb7429 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -455,8 +455,9 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Odomknúť a použiť"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Pri načítavaní kariet sa vyskytol problém. Skúste to neskôr."</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Nastavenia uzamknutej obrazovky"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"Skenovanie QR kódu"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"Kliknutím naskenujte QR kód"</string>
+ <string name="qr_code_scanner_title" msgid="5660820608548306581">"QR kód"</string>
+ <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+ <skip />
<string name="status_bar_work" msgid="5238641949837091056">"Pracovný profil"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Režim v lietadle"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Váš budík o <xliff:g id="WHEN">%1$s</xliff:g> sa nespustí"</string>
@@ -803,7 +804,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Prehrať skladbu <xliff:g id="SONG_NAME">%1$s</xliff:g> od interpreta <xliff:g id="ARTIST_NAME">%2$s</xliff:g> z aplikácie <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Prehrať skladbu <xliff:g id="SONG_NAME">%1$s</xliff:g> z aplikácie <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Späť"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Ak chcete prehrávať v zariadení <xliff:g id="DEVICENAME">%1$s</xliff:g>, priblížte sa"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"Prehráva sa v zariadení <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Neaktívne, preverte aplikáciu"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Nenájdené"</string>
@@ -891,4 +893,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Vyberte používateľa"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"Aplikácie spustené na pozadí"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Ukončiť"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 26d0570b8577..9c2c32c928b3 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -455,8 +455,9 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Odklenite za uporabo"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Pri pridobivanju kartic je prišlo do težave. Poskusite znova pozneje."</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Nastavitve zaklepanja zaslona"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"Optično branje kode QR"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"Kliknite, če želite optično prebrati kodo QR"</string>
+ <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+ <skip />
+ <string name="qr_code_scanner_description" msgid="7937603775306661863">"Dotaknite se za optično branje"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Profil za Android Work"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Način za letalo"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Naslednjega alarma ob <xliff:g id="WHEN">%1$s</xliff:g> ne boste slišali"</string>
@@ -803,7 +804,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Predvajaj skladbo <xliff:g id="SONG_NAME">%1$s</xliff:g> izvajalca <xliff:g id="ARTIST_NAME">%2$s</xliff:g> iz aplikacije <xliff:g id="APP_LABEL">%3$s</xliff:g>."</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Predvajaj skladbo <xliff:g id="SONG_NAME">%1$s</xliff:g> iz aplikacije <xliff:g id="APP_LABEL">%2$s</xliff:g>."</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Razveljavi"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Za predvajanje v napravi <xliff:g id="DEVICENAME">%1$s</xliff:g> bolj približajte telefon."</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"Predvajanje v napravi <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Neaktivno, poglejte aplikacijo"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Ni mogoče najti"</string>
@@ -891,4 +893,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Izberite uporabnika"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"Aplikacije z izvajanjem v ozadju"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Ustavi"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index 703e681a3762..993ab0cee711 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -233,8 +233,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi nuk është lidhur"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Ndriçimi"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Anasjellja e ngjyrës"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Korrigjimi i ngjyrës"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Cilësime të tjera"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Cilësimet e përdoruesit"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"U krye"</string>
@@ -450,8 +449,10 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Shkyçe për ta përdorur"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Pati një problem me marrjen e kartave të tua. Provo përsëri më vonë"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Cilësimet e ekranit të kyçjes"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"Skano kodin QR"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"Kliko për të skanuar një kod QR"</string>
+ <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+ <skip />
+ <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+ <skip />
<string name="status_bar_work" msgid="5238641949837091056">"Profili i punës"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Modaliteti i aeroplanit"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Nuk do ta dëgjosh alarmin e radhës në <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -792,7 +793,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Luaj <xliff:g id="SONG_NAME">%1$s</xliff:g> nga <xliff:g id="ARTIST_NAME">%2$s</xliff:g> nga <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Luaj <xliff:g id="SONG_NAME">%1$s</xliff:g> nga <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Zhbëj"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Afrohu për të luajtur në <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"Po luhet në <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Joaktive, kontrollo aplikacionin"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Nuk u gjet"</string>
@@ -880,4 +882,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Zgjidh përdoruesin"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"Aplikacionet që ekzekutohen në sfond"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Ndalo"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-sq/tiles_states_strings.xml b/packages/SystemUI/res/values-sq/tiles_states_strings.xml
index 461cd93b3ba9..c7e3883aa42e 100644
--- a/packages/SystemUI/res/values-sq/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-sq/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"Joaktive"</item>
<item msgid="2075645297847971154">"Aktive"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"Nuk ofrohet"</item>
+ <item msgid="1909756493418256167">"Joaktiv"</item>
+ <item msgid="4531508423703413340">"Aktiv"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"Nuk ofrohet"</item>
<item msgid="9103697205127645916">"Joaktiv"</item>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index bf325e2b2db7..f637f2cf3bb7 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -234,8 +234,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"WiFi није повезан"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Осветљеност"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Инверзија боја"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Корекција боја"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Још подешавања"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Корисничка подешавања"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Готово"</string>
@@ -453,8 +452,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Откључај ради коришћења"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Дошло је до проблема при преузимању картица. Пробајте поново касније"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Подешавања закључаног екрана"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"Скенирај QR кôд"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"Кликните да бисте скенирали QR кôд"</string>
+ <string name="qr_code_scanner_title" msgid="5660820608548306581">"QR кôд"</string>
+ <string name="qr_code_scanner_description" msgid="7937603775306661863">"Додирните да бисте скенирали"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Пословни профил"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Режим рада у авиону"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Нећете чути следећи аларм у <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -798,7 +797,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Пустите <xliff:g id="SONG_NAME">%1$s</xliff:g> извођача <xliff:g id="ARTIST_NAME">%2$s</xliff:g> из апликације <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Пустите <xliff:g id="SONG_NAME">%1$s</xliff:g> из апликације <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Опозови"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Приближите да бисте пуштали музику на: <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"Пушта се на: <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Неактивно. Видите апликацију"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Није пронађено"</string>
@@ -886,4 +886,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Изаберите корисника"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"Апликације покренуте у позадини"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Заустави"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-sr/tiles_states_strings.xml b/packages/SystemUI/res/values-sr/tiles_states_strings.xml
index ab10ca1f6881..fda7465bcadf 100644
--- a/packages/SystemUI/res/values-sr/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-sr/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"Искључено"</item>
<item msgid="2075645297847971154">"Укључено"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"Недоступно"</item>
+ <item msgid="1909756493418256167">"Искључено"</item>
+ <item msgid="4531508423703413340">"Укључено"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"Недоступно"</item>
<item msgid="9103697205127645916">"Искључено"</item>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 36f4ab193bec..04cdcbe30f0c 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -233,8 +233,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Ej ansluten till wifi"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Ljusstyrka"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Färginvertering"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Färgkorrigering"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Fler inställningar"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Användarinställningar"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Klart"</string>
@@ -450,8 +449,9 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Lås upp för att använda"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Det gick inte att hämta dina kort. Försök igen senare."</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Inställningar för låsskärm"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"Skanna QR"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"Klicka för att skanna en QR-kod"</string>
+ <string name="qr_code_scanner_title" msgid="5660820608548306581">"QR-kod"</string>
+ <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+ <skip />
<string name="status_bar_work" msgid="5238641949837091056">"Jobbprofil"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Flygplansläge"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Nästa alarm, kl. <xliff:g id="WHEN">%1$s</xliff:g>, kommer inte att höras"</string>
@@ -792,7 +792,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Spela upp <xliff:g id="SONG_NAME">%1$s</xliff:g> med <xliff:g id="ARTIST_NAME">%2$s</xliff:g> från <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Spela upp <xliff:g id="SONG_NAME">%1$s</xliff:g> från <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Ångra"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Flytta närmare för att spela upp på <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"Spelas upp på <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Inaktiv, kolla appen"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Hittades inte"</string>
@@ -880,4 +881,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Välj användare"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"Appar som körs i bakgrunden"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Stoppa"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-sv/tiles_states_strings.xml b/packages/SystemUI/res/values-sv/tiles_states_strings.xml
index cdcf6e6c5f41..11026981c1a0 100644
--- a/packages/SystemUI/res/values-sv/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-sv/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"Av"</item>
<item msgid="2075645297847971154">"På"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"Inte tillgängligt"</item>
+ <item msgid="1909756493418256167">"Av"</item>
+ <item msgid="4531508423703413340">"På"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"Inte tillgängligt"</item>
<item msgid="9103697205127645916">"Av"</item>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 6643348c4bdd..2618484925e6 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -233,8 +233,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi haijaunganishwa"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Ung\'avu"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Ugeuzaji rangi"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Usahihishaji wa rangirangi"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Mipangilio zaidi"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Mipangilio ya mtumiaji"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Nimemaliza"</string>
@@ -450,8 +449,10 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Fungua ili utumie"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Hitilafu imetokea wakati wa kuleta kadi zako, tafadhali jaribu tena baadaye"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Mipangilio ya kufunga skrini"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"Changanua QR"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"Bofya ili uchanganue msimbo wa QR"</string>
+ <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+ <skip />
+ <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+ <skip />
<string name="status_bar_work" msgid="5238641949837091056">"Wasifu wa kazini"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Hali ya ndegeni"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Hutasikia kengele yako inayofuata ya saa <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -792,7 +793,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Cheza <xliff:g id="SONG_NAME">%1$s</xliff:g> ulioimbwa na <xliff:g id="ARTIST_NAME">%2$s</xliff:g> katika <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Cheza <xliff:g id="SONG_NAME">%1$s</xliff:g> katika <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Tendua"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Sogea karibu ili ucheze kwenye <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"Inacheza kwenye <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Haitumiki, angalia programu"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Hakipatikani"</string>
@@ -880,4 +882,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Chagua mtumiaji"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"Programu zinazotumika chinichini"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Simamisha"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-sw/tiles_states_strings.xml b/packages/SystemUI/res/values-sw/tiles_states_strings.xml
index 563c07803d12..d186d5192855 100644
--- a/packages/SystemUI/res/values-sw/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-sw/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"Kimezimwa"</item>
<item msgid="2075645297847971154">"Kimewashwa"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"Haupatikani"</item>
+ <item msgid="1909756493418256167">"Umezimwa"</item>
+ <item msgid="4531508423703413340">"Umewashwa"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"Hakipatikani"</item>
<item msgid="9103697205127645916">"Kimezimwa"</item>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index f721b9848b5e..e45f0721676f 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -233,8 +233,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"வைஃபை இணைக்கப்படவில்லை"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"ஒளிர்வு"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"கலர் இன்வெர்ஷன்"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"கலர் கரெக்‌ஷன்"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"அமைப்பில் மாற்று"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"பயனர் அமைப்புகள்"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"முடிந்தது"</string>
@@ -450,8 +449,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"பயன்படுத்துவதற்கு அன்லாக் செய்க"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"உங்கள் கார்டுகளின் விவரங்களைப் பெறுவதில் சிக்கல் ஏற்பட்டது, பிறகு முயலவும்"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"பூட்டுத் திரை அமைப்புகள்"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR குறியீட்டை ஸ்கேன் செய்யுங்கள்"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"QR குறியீட்டை ஸ்கேன் செய்யக் கிளிக் செய்யவும்"</string>
+ <string name="qr_code_scanner_title" msgid="5660820608548306581">"QR குறியீடு"</string>
+ <string name="qr_code_scanner_description" msgid="7937603775306661863">"ஸ்கேன் செய்யத் தட்டவும்"</string>
<string name="status_bar_work" msgid="5238641949837091056">"பணிக் கணக்கு"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"விமானப் பயன்முறை"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"அடுத்த அலாரத்தை <xliff:g id="WHEN">%1$s</xliff:g> மணிக்கு கேட்க மாட்டீர்கள்"</string>
@@ -792,7 +791,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="ARTIST_NAME">%2$s</xliff:g> இன் <xliff:g id="SONG_NAME">%1$s</xliff:g> பாடலை <xliff:g id="APP_LABEL">%3$s</xliff:g> ஆப்ஸில் பிளேசெய்"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="SONG_NAME">%1$s</xliff:g> பாடலை <xliff:g id="APP_LABEL">%2$s</xliff:g> ஆப்ஸில் பிளேசெய்"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"செயல்தவிர்"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"<xliff:g id="DEVICENAME">%1$s</xliff:g> சாதனத்தில் பிளே செய்ய உங்கள் சாதனத்தை அருகில் எடுத்துச் செல்லவும்"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"<xliff:g id="DEVICENAME">%1$s</xliff:g> சாதனத்தில் பிளே ஆகிறது"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"செயலில் இல்லை , சரிபார்க்கவும்"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"இல்லை"</string>
@@ -880,4 +880,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"பயனரைத் தேர்வுசெய்க"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"பின்னணியில் இயங்கும் ஆப்ஸ்"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"நிறுத்து"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ta/tiles_states_strings.xml b/packages/SystemUI/res/values-ta/tiles_states_strings.xml
index a9e0eabc3d8c..0883d2202dfb 100644
--- a/packages/SystemUI/res/values-ta/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ta/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"முடக்கப்பட்டுள்ளது"</item>
<item msgid="2075645297847971154">"இயக்கப்பட்டுள்ளது"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"இல்லை"</item>
+ <item msgid="1909756493418256167">"முடக்கப்பட்டுள்ளது"</item>
+ <item msgid="4531508423703413340">"இயக்கப்பட்டுள்ளது"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"கிடைக்கவில்லை"</item>
<item msgid="9103697205127645916">"முடக்கப்பட்டுள்ளது"</item>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index 501ee1753815..4e4102358fb1 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -233,8 +233,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi‑Fi కనెక్ట్ కాలేదు"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"ప్రకాశం"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"కలర్ మార్పిడి"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"కలర్ కరెక్షన్"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"మరిన్ని సెట్టింగ్‌లు"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"యూజర్ సెట్టింగ్‌లు"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"పూర్తయింది"</string>
@@ -450,8 +449,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"ఉపయోగించడానికి అన్‌లాక్ చేయండి"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"మీ కార్డ్‌లను పొందడంలో సమస్య ఉంది, దయచేసి తర్వాత మళ్లీ ట్రై చేయండి"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"లాక్ స్క్రీన్ సెట్టింగ్‌లు"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"QRను స్కాన్ చేయండి"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"QR కోడ్‌ను స్కాన్ చేయడానికి క్లిక్ చేయండి"</string>
+ <string name="qr_code_scanner_title" msgid="5660820608548306581">"QR కోడ్"</string>
+ <string name="qr_code_scanner_description" msgid="7937603775306661863">"స్కాన్ చేయడానికి ట్యాప్ చేయండి"</string>
<string name="status_bar_work" msgid="5238641949837091056">"ఆఫీస్ ప్రొఫైల్‌"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"ఎయిర్‌ప్లేన్ మోడ్"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"మీరు <xliff:g id="WHEN">%1$s</xliff:g> సెట్ చేసిన మీ తర్వాత అలారం మీకు వినిపించదు"</string>
@@ -792,7 +791,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="APP_LABEL">%3$s</xliff:g> నుండి <xliff:g id="ARTIST_NAME">%2$s</xliff:g> పాడిన <xliff:g id="SONG_NAME">%1$s</xliff:g>‌ను ప్లే చేయండి"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="APP_LABEL">%2$s</xliff:g> నుండి <xliff:g id="SONG_NAME">%1$s</xliff:g>‌ను ప్లే చేయండి"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"చర్య రద్దు"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"<xliff:g id="DEVICENAME">%1$s</xliff:g>లో ప్లే చేయడానికి దగ్గరగా వెళ్లండి"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"<xliff:g id="DEVICENAME">%1$s</xliff:g>లో ప్లే అవుతోంది"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"ఇన్‌యాక్టివ్, యాప్ చెక్ చేయండి"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"కనుగొనబడలేదు"</string>
@@ -880,4 +880,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"యూజర్‌ను ఎంచుకోండి"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"యాప్‌లు బ్యాక్‌గ్రౌండ్‌లో రన్ అవుతున్నాయి"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"ఆపివేయండి"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-te/tiles_states_strings.xml b/packages/SystemUI/res/values-te/tiles_states_strings.xml
index 4cb7291cafd3..5c8ae3da6d9f 100644
--- a/packages/SystemUI/res/values-te/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-te/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"ఆఫ్‌లో ఉంది"</item>
<item msgid="2075645297847971154">"ఆన్‌లో ఉంది"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"అందుబాటులో లేదు"</item>
+ <item msgid="1909756493418256167">"ఆఫ్‌లో ఉంది"</item>
+ <item msgid="4531508423703413340">"ఆన్‌లో ఉంది"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"అందుబాటులో లేదు"</item>
<item msgid="9103697205127645916">"ఆఫ్‌లో ఉంది"</item>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 6b7c05f47d4e..8897186e4a10 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -233,8 +233,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"ไม่ได้เชื่อมต่อ Wi-Fi"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"ความสว่าง"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"การกลับสี"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"การแก้สี"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"การตั้งค่าเพิ่มเติม"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"การตั้งค่าของผู้ใช้"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"เสร็จสิ้น"</string>
@@ -450,8 +449,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"ปลดล็อกเพื่อใช้"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"เกิดปัญหาในการดึงข้อมูลบัตรของคุณ โปรดลองอีกครั้งในภายหลัง"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"การตั้งค่าหน้าจอล็อก"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"สแกนคิวอาร์"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"คลิกเพื่อสแกนคิวอาร์โค้ด"</string>
+ <string name="qr_code_scanner_title" msgid="5660820608548306581">"คิวอาร์โค้ด"</string>
+ <string name="qr_code_scanner_description" msgid="7937603775306661863">"แตะเพื่อสแกน"</string>
<string name="status_bar_work" msgid="5238641949837091056">"โปรไฟล์งาน"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"โหมดบนเครื่องบิน"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"คุณจะไม่ได้ยินเสียงปลุกครั้งถัดไปในเวลา <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -792,7 +791,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"เปิดเพลง <xliff:g id="SONG_NAME">%1$s</xliff:g> ของ <xliff:g id="ARTIST_NAME">%2$s</xliff:g> จาก <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"เปิดเพลง <xliff:g id="SONG_NAME">%1$s</xliff:g> จาก <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"เลิกทำ"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"ขยับเข้ามาใกล้ขึ้นเพื่อเล่นใน <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"กำลังเล่นใน <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"ไม่มีการใช้งาน โปรดตรวจสอบแอป"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"ไม่พบ"</string>
@@ -880,4 +880,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"เลือกผู้ใช้"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"แอปที่ทำงานอยู่เบื้องหลัง"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"หยุด"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-th/tiles_states_strings.xml b/packages/SystemUI/res/values-th/tiles_states_strings.xml
index 67768736afd9..e7eae732ba46 100644
--- a/packages/SystemUI/res/values-th/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-th/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"ปิด"</item>
<item msgid="2075645297847971154">"เปิด"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"ไม่พร้อมใช้งาน"</item>
+ <item msgid="1909756493418256167">"ปิด"</item>
+ <item msgid="4531508423703413340">"เปิด"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"ไม่พร้อมใช้งาน"</item>
<item msgid="9103697205127645916">"ปิด"</item>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index df546587e5b9..6ab6ef661ab4 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -233,8 +233,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Hindi nakakonekta sa Wi-Fi"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Brightness"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Pag-invert ng kulay"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Pagtatama ng kulay"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Higit pang setting"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Mga setting ng user"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Tapos na"</string>
@@ -450,8 +449,10 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"I-unlock para magamit"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Nagkaproblema sa pagkuha ng iyong mga card, pakisubukan ulit sa ibang pagkakataon"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Mga setting ng lock screen"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"I-scan ang QR"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"Mag-click para mag-scan ng QR code"</string>
+ <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+ <skip />
+ <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+ <skip />
<string name="status_bar_work" msgid="5238641949837091056">"Profile sa trabaho"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Airplane mode"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Hindi mo maririnig ang iyong susunod na alarm ng <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -792,7 +793,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"I-play ang <xliff:g id="SONG_NAME">%1$s</xliff:g> ni/ng <xliff:g id="ARTIST_NAME">%2$s</xliff:g> mula sa <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"I-play ang <xliff:g id="SONG_NAME">%1$s</xliff:g> mula sa <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"I-undo"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Lumapit pa para mag-play sa <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"Nagpe-play sa <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Hindi aktibo, tingnan ang app"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Hindi nahanap"</string>
@@ -880,4 +882,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Pumili ng user"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"Mga app na tumatakbo sa background"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Ihinto"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-tl/tiles_states_strings.xml b/packages/SystemUI/res/values-tl/tiles_states_strings.xml
index 62172a4dc907..f33d8a2c3180 100644
--- a/packages/SystemUI/res/values-tl/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-tl/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"Naka-off"</item>
<item msgid="2075645297847971154">"Naka-on"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"Hindi available"</item>
+ <item msgid="1909756493418256167">"Naka-off"</item>
+ <item msgid="4531508423703413340">"Naka-on"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"Hindi available"</item>
<item msgid="9103697205127645916">"Naka-off"</item>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index f389bf17de5a..ee44b76da88d 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -233,8 +233,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Kablosuz ağ bağlı değil"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Parlaklık"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Rengi ters çevirme"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Renk düzeltme"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Diğer ayarlar"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Kullanıcı ayarları"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Bitti"</string>
@@ -450,8 +449,10 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Kullanmak için kilidi aç"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Kartlarınız alınırken bir sorun oluştu. Lütfen daha sonra tekrar deneyin"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Kilit ekranı ayarları"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR kodunu tarayın"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"QR kodu taramak için tıklayın"</string>
+ <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+ <skip />
+ <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+ <skip />
<string name="status_bar_work" msgid="5238641949837091056">"İş profili"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Uçak modu"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"<xliff:g id="WHEN">%1$s</xliff:g> olarak ayarlanmış bir sonraki alarmınızı duymayacaksınız"</string>
@@ -792,7 +793,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="APP_LABEL">%3$s</xliff:g> uygulamasından <xliff:g id="ARTIST_NAME">%2$s</xliff:g>, <xliff:g id="SONG_NAME">%1$s</xliff:g> şarkısını çal"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="APP_LABEL">%2$s</xliff:g> uygulamasından <xliff:g id="SONG_NAME">%1$s</xliff:g> şarkısını çal"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Geri al"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"<xliff:g id="DEVICENAME">%1$s</xliff:g> cihazında çalmak için yaklaşın"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"<xliff:g id="DEVICENAME">%1$s</xliff:g> cihazında çalınıyor"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Devre dışı, uygulamaya bakın"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Bulunamadı"</string>
@@ -880,4 +882,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Kullanıcı seçin"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"Arka planda çalışan uygulamalar"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Durdur"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-tr/tiles_states_strings.xml b/packages/SystemUI/res/values-tr/tiles_states_strings.xml
index 9f836fc6bc2d..17b4bb4e1a44 100644
--- a/packages/SystemUI/res/values-tr/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-tr/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"Kapalı"</item>
<item msgid="2075645297847971154">"Açık"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"Bilinmiyor"</item>
+ <item msgid="1909756493418256167">"Kapalı"</item>
+ <item msgid="4531508423703413340">"Açık"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"Kullanılamıyor"</item>
<item msgid="9103697205127645916">"Kapalı"</item>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 3856cc5fbaf3..27aad2edfdb8 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -235,8 +235,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Wi-Fi не під’єднано"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Яскравість"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Інверсія кольорів"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Корекція кольору"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Більше налаштувань"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Налаштування користувача"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Готово"</string>
@@ -456,8 +455,10 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Розблокувати, щоб використовувати"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Не вдалось отримати ваші картки. Повторіть спробу пізніше."</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Параметри блокування екрана"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"Сканувати QR-код"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"Натисніть, щоб відсканувати QR-код"</string>
+ <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+ <skip />
+ <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+ <skip />
<string name="status_bar_work" msgid="5238641949837091056">"Робочий профіль"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Режим польоту"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Наступний сигнал о <xliff:g id="WHEN">%1$s</xliff:g> не пролунає"</string>
@@ -804,7 +805,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Увімкнути пісню \"<xliff:g id="SONG_NAME">%1$s</xliff:g>\", яку виконує <xliff:g id="ARTIST_NAME">%2$s</xliff:g>, у додатку <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Увімкнути пісню \"<xliff:g id="SONG_NAME">%1$s</xliff:g>\" у додатку <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Відмінити"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Щоб відтворити на пристрої <xliff:g id="DEVICENAME">%1$s</xliff:g>, наблизьтеся до нього"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"Відтворюється на пристрої <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Неактивно, перейдіть у додаток"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Не знайдено"</string>
@@ -892,4 +894,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Виберіть користувача"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"Додатки, що працюють у фоновому режимі"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Зупинити"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-uk/tiles_states_strings.xml b/packages/SystemUI/res/values-uk/tiles_states_strings.xml
index 34c40d3d2951..c4ac1949a4c6 100644
--- a/packages/SystemUI/res/values-uk/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-uk/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"Вимкнено"</item>
<item msgid="2075645297847971154">"Увімкнено"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"Недоступно"</item>
+ <item msgid="1909756493418256167">"Вимкнено"</item>
+ <item msgid="4531508423703413340">"Увімкнено"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"Недоступно"</item>
<item msgid="9103697205127645916">"Вимкнено"</item>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index dafaddf1bd66..015d52072f01 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -233,8 +233,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"‏Wi-Fi سے منسلک نہیں ہے"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"چمکیلا پن"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"رنگوں کی تقلیب"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"رنگ کی اصلاح"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"مزید ترتیبات"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"صارف کی ترتیبات"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"ہو گیا"</string>
@@ -450,8 +449,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"استعمال کرنے کے لیے غیر مقفل کریں"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"آپ کے کارڈز حاصل کرنے میں ایک مسئلہ درپیش تھا، براہ کرم بعد میں دوبارہ کوشش کریں"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"مقفل اسکرین کی ترتیبات"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"‏QR اسکین کریں"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"‏QR کوڈ اسکین کرنے کے لیے کلک کریں"</string>
+ <string name="qr_code_scanner_title" msgid="5660820608548306581">"‏QR کوڈ"</string>
+ <string name="qr_code_scanner_description" msgid="7937603775306661863">"اسکین کرنے کے لیے تھپتھپائیں"</string>
<string name="status_bar_work" msgid="5238641949837091056">"دفتری پروفائل"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"ہوائی جہاز وضع"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"آپ کو <xliff:g id="WHEN">%1$s</xliff:g> بجے اپنا اگلا الارم سنائی نہیں دے گا"</string>
@@ -792,7 +791,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="APP_LABEL">%3$s</xliff:g> سے <xliff:g id="ARTIST_NAME">%2$s</xliff:g> کا <xliff:g id="SONG_NAME">%1$s</xliff:g> چلائیں"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="APP_LABEL">%2$s</xliff:g> سے <xliff:g id="SONG_NAME">%1$s</xliff:g> چلائیں"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"کالعدم کریں"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"<xliff:g id="DEVICENAME">%1$s</xliff:g> پر چلانے کے لیے قریب کریں"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"<xliff:g id="DEVICENAME">%1$s</xliff:g> پر چل رہا ہے"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"غیر فعال، ایپ چیک کریں"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"نہیں ملا"</string>
@@ -880,4 +880,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"صارف منتخب کریں"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"ایپس پس منظر میں چل رہی ہیں"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"روکیں"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ur/tiles_states_strings.xml b/packages/SystemUI/res/values-ur/tiles_states_strings.xml
index 02943fa8e272..155403151ed7 100644
--- a/packages/SystemUI/res/values-ur/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ur/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"آف ہے"</item>
<item msgid="2075645297847971154">"آن ہے"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"غیر دستیاب"</item>
+ <item msgid="1909756493418256167">"آف"</item>
+ <item msgid="4531508423703413340">"آن"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"دستیاب نہیں ہے"</item>
<item msgid="9103697205127645916">"آف ہے"</item>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index 13971dd28cf2..ce7fc3245925 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -449,8 +449,10 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Foydalanish uchun qulfdan chiqarish"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Bildirgilarni yuklashda xatolik yuz berdi, keyinroq qaytadan urining"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Qulflangan ekran sozlamalari"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"QR kodni skanerlash"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"QR kodni skanerlash uchun bosing"</string>
+ <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+ <skip />
+ <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+ <skip />
<string name="status_bar_work" msgid="5238641949837091056">"Ish profili"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Parvoz rejimi"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Keyingi signal (<xliff:g id="WHEN">%1$s</xliff:g>) chalinmaydi"</string>
@@ -791,7 +793,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"<xliff:g id="APP_LABEL">%3$s</xliff:g> ilovasida ijro etish: <xliff:g id="SONG_NAME">%1$s</xliff:g> – <xliff:g id="ARTIST_NAME">%2$s</xliff:g>"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"<xliff:g id="APP_LABEL">%2$s</xliff:g> ilovasida ijro etilmoqda: <xliff:g id="SONG_NAME">%1$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Qaytarish"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"<xliff:g id="DEVICENAME">%1$s</xliff:g>da ijro etish uchun yaqinroq keling"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"<xliff:g id="DEVICENAME">%1$s</xliff:g> qurilmasida ijro qilinmoqda"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Nofaol. Ilovani tekshiring"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Topilmadi"</string>
@@ -879,4 +882,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Foydalanuvchini tanlang"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"Orqa fonda ishlayotgan ilovalar"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Stop"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 4d2c90c35216..e4066d9ec47e 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -233,8 +233,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"Chưa kết nối với Wi-Fi"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Độ sáng"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Đảo màu"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Chỉnh màu"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Chế độ cài đặt khác"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Cài đặt người dùng"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Xong"</string>
@@ -450,8 +449,10 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Mở khóa để sử dụng"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Đã xảy ra sự cố khi tải thẻ của bạn. Vui lòng thử lại sau"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Cài đặt màn hình khóa"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"Quét mã QR"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"Nhấp để quét mã QR"</string>
+ <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+ <skip />
+ <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+ <skip />
<string name="status_bar_work" msgid="5238641949837091056">"Hồ sơ công việc"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Chế độ máy bay"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Bạn sẽ không nghe thấy báo thức tiếp theo lúc <xliff:g id="WHEN">%1$s</xliff:g> của mình"</string>
@@ -792,7 +793,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Phát <xliff:g id="SONG_NAME">%1$s</xliff:g> của <xliff:g id="ARTIST_NAME">%2$s</xliff:g> trên <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Phát <xliff:g id="SONG_NAME">%1$s</xliff:g> trên <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Hủy"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Đưa thiết bị đến gần hơn để phát trên <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"Đang phát trên <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Không hoạt động, hãy kiểm tra ứng dụng"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Không tìm thấy"</string>
@@ -880,4 +882,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Chọn người dùng"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"Các ứng dụng chạy trong nền"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Dừng"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-vi/tiles_states_strings.xml b/packages/SystemUI/res/values-vi/tiles_states_strings.xml
index bff64ab6b6d3..94e801278637 100644
--- a/packages/SystemUI/res/values-vi/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-vi/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"Đang tắt"</item>
<item msgid="2075645297847971154">"Đang bật"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"Không có"</item>
+ <item msgid="1909756493418256167">"Đang tắt"</item>
+ <item msgid="4531508423703413340">"Đang bật"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"Không hoạt động"</item>
<item msgid="9103697205127645916">"Đang tắt"</item>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 038ada0bec29..4666a882f8a3 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -233,8 +233,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"未连接到 WLAN 网络"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"亮度"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"颜色反转"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"色彩校正"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"更多设置"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"用户设置"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"完成"</string>
@@ -450,8 +449,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"解锁设备即可使用"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"获取您的卡片时出现问题,请稍后重试"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"锁定屏幕设置"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"扫描二维码"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"点击即可扫描二维码"</string>
+ <string name="qr_code_scanner_title" msgid="5660820608548306581">"二维码"</string>
+ <string name="qr_code_scanner_description" msgid="7937603775306661863">"点按即可扫描"</string>
<string name="status_bar_work" msgid="5238641949837091056">"工作资料"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"飞行模式"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"您在<xliff:g id="WHEN">%1$s</xliff:g>将不会听到下次闹钟响铃"</string>
@@ -792,7 +791,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"通过<xliff:g id="APP_LABEL">%3$s</xliff:g>播放<xliff:g id="ARTIST_NAME">%2$s</xliff:g>的《<xliff:g id="SONG_NAME">%1$s</xliff:g>》"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"通过<xliff:g id="APP_LABEL">%2$s</xliff:g>播放《<xliff:g id="SONG_NAME">%1$s</xliff:g>》"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"撤消"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"移近一点以在“<xliff:g id="DEVICENAME">%1$s</xliff:g>”上播放"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"正在“<xliff:g id="DEVICENAME">%1$s</xliff:g>”上播放"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"无效,请检查应用"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"未找到"</string>
@@ -880,4 +880,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"选择用户"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"正在在后台运行的应用"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"停止"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml b/packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml
index 0d72f61092d0..a266d929ec3f 100644
--- a/packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"已关闭"</item>
<item msgid="2075645297847971154">"已开启"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"不可用"</item>
+ <item msgid="1909756493418256167">"关闭"</item>
+ <item msgid="4531508423703413340">"开启"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"不可用"</item>
<item msgid="9103697205127645916">"已关闭"</item>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index d4406b4b111b..f64e78cab621 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -233,8 +233,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"未連線至 Wi-Fi"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"亮度"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"色彩反轉"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"色彩校正"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"更多設定"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"使用者設定"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"完成"</string>
@@ -450,8 +449,8 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"解鎖即可使用"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"擷取資訊卡時發生問題,請稍後再試。"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"上鎖畫面設定"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"掃瞄 QR 碼"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"按一下即可掃瞄 QR 碼"</string>
+ <string name="qr_code_scanner_title" msgid="5660820608548306581">"QR 碼"</string>
+ <string name="qr_code_scanner_description" msgid="7937603775306661863">"輕按即可掃瞄"</string>
<string name="status_bar_work" msgid="5238641949837091056">"工作設定檔"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"飛行模式"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"您不會<xliff:g id="WHEN">%1$s</xliff:g>聽到鬧鐘"</string>
@@ -792,7 +791,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"在 <xliff:g id="APP_LABEL">%3$s</xliff:g> 播放 <xliff:g id="ARTIST_NAME">%2$s</xliff:g> 的《<xliff:g id="SONG_NAME">%1$s</xliff:g>》"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"在 <xliff:g id="APP_LABEL">%2$s</xliff:g> 播放《<xliff:g id="SONG_NAME">%1$s</xliff:g>》"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"復原"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"移近一點以在 <xliff:g id="DEVICENAME">%1$s</xliff:g> 上播放"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"正在 <xliff:g id="DEVICENAME">%1$s</xliff:g> 上播放"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"已停用,請檢查應用程式"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"找不到"</string>
@@ -880,4 +880,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"選取使用者"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"正在背景中執行的應用程式"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"停止"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml b/packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml
index 571cfba728a4..d5d092f3067d 100644
--- a/packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"已關閉"</item>
<item msgid="2075645297847971154">"已開啟"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"無法使用"</item>
+ <item msgid="1909756493418256167">"關閉"</item>
+ <item msgid="4531508423703413340">"開啟"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"無法使用"</item>
<item msgid="9103697205127645916">"已關閉"</item>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 83623d434a27..87a6ae828383 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -233,8 +233,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"未連線至 Wi-Fi"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"亮度"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"色彩反轉"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"色彩校正"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"更多設定"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"使用者設定"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"完成"</string>
@@ -450,8 +449,9 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"解鎖即可使用"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"擷取卡片時發生問題,請稍後再試"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"螢幕鎖定設定"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"掃描 QR 圖碼"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"按一下即可掃描 QR 圖碼"</string>
+ <string name="qr_code_scanner_title" msgid="5660820608548306581">"QR 圖碼"</string>
+ <!-- no translation found for qr_code_scanner_description (7937603775306661863) -->
+ <skip />
<string name="status_bar_work" msgid="5238641949837091056">"工作資料夾"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"飛航模式"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"你不會聽到下一個<xliff:g id="WHEN">%1$s</xliff:g> 的鬧鐘"</string>
@@ -792,7 +792,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"透過「<xliff:g id="APP_LABEL">%3$s</xliff:g>」播放<xliff:g id="ARTIST_NAME">%2$s</xliff:g>的〈<xliff:g id="SONG_NAME">%1$s</xliff:g>〉"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"透過「<xliff:g id="APP_LABEL">%2$s</xliff:g>」播放〈<xliff:g id="SONG_NAME">%1$s</xliff:g>〉"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"復原"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"如要在「<xliff:g id="DEVICENAME">%1$s</xliff:g>」上播放,請靠近這部裝置"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"正在「<xliff:g id="DEVICENAME">%1$s</xliff:g>」上播放"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"無效,請查看應用程式"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"找不到控制項"</string>
@@ -880,4 +881,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"選取使用者"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"目前在背景執行的應用程式"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"停止"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-zh-rTW/tiles_states_strings.xml b/packages/SystemUI/res/values-zh-rTW/tiles_states_strings.xml
index 48896579101a..ad2441344256 100644
--- a/packages/SystemUI/res/values-zh-rTW/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"已關閉"</item>
<item msgid="2075645297847971154">"已開啟"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"無法使用"</item>
+ <item msgid="1909756493418256167">"已關閉"</item>
+ <item msgid="4531508423703413340">"已開啟"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"無法使用"</item>
<item msgid="9103697205127645916">"已關閉"</item>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index fa200dcefaa2..602845b36c3d 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -233,8 +233,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"I-Wi-Fi ayixhunyiwe"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Ukugqama"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Ukuguqulwa kombala"</string>
- <!-- no translation found for quick_settings_color_correction_label (5636617913560474664) -->
- <skip />
+ <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Ukulungiswa kombala"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Izilungiselelo eziningi"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Amasethingi womsebenzisi"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Kwenziwe"</string>
@@ -450,8 +449,9 @@
<string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Vula ukuze usebenzise"</string>
<string name="wallet_error_generic" msgid="257704570182963611">"Kube khona inkinga yokuthola amakhadi akho, sicela uzame futhi ngemuva kwesikhathi"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Amasethingi okukhiya isikrini"</string>
- <string name="qr_code_scanner_title" msgid="1598912458255252498">"Skena i-QR"</string>
- <string name="qr_code_scanner_description" msgid="7452098243938659945">"Chofoza ukuze uskene ikhodi ye-QR"</string>
+ <!-- no translation found for qr_code_scanner_title (5660820608548306581) -->
+ <skip />
+ <string name="qr_code_scanner_description" msgid="7937603775306661863">"Thepha ukuze uskene"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Iphrofayela yomsebenzi"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Imodi yendiza"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"Ngeke uzwe i-alamu yakho elandelayo ngo-<xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -792,7 +792,8 @@
<string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Dlala i-<xliff:g id="SONG_NAME">%1$s</xliff:g> ka-<xliff:g id="ARTIST_NAME">%2$s</xliff:g> kusuka ku-<xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
<string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Dlala i-<xliff:g id="SONG_NAME">%1$s</xliff:g> kusuka ku-<xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
<string name="media_transfer_undo" msgid="1895606387620728736">"Hlehlisa"</string>
- <string name="media_move_closer_to_transfer" msgid="1628426856415585141">"Sondeza eduze ukudlala ku-<xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for media_move_closer_to_start_cast (2673104707465013176) -->
+ <skip />
<string name="media_transfer_playing" msgid="3760048096352107789">"Idlala ku-<xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Akusebenzi, hlola uhlelo lokusebenza"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Ayitholakali"</string>
@@ -880,4 +881,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Khetha umsebenzisi"</string>
<string name="fgs_manager_dialog_title" msgid="656091833603337197">"Ama-app ayaqhubeka ngemuva"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Misa"</string>
+ <!-- no translation found for clipboard_edit_text_copy (770856373439969178) -->
+ <skip />
+ <!-- no translation found for clipboard_overlay_text_copied (1872624400464891363) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-zu/tiles_states_strings.xml b/packages/SystemUI/res/values-zu/tiles_states_strings.xml
index ea40faf4eb56..92290d6d1a0c 100644
--- a/packages/SystemUI/res/values-zu/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-zu/tiles_states_strings.xml
@@ -86,9 +86,11 @@
<item msgid="5715725170633593906">"Valiwe"</item>
<item msgid="2075645297847971154">"Vuliwe"</item>
</string-array>
- <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) -->
- <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) -->
- <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) -->
+ <string-array name="tile_states_color_correction">
+ <item msgid="2840507878437297682">"Akutholakali"</item>
+ <item msgid="1909756493418256167">"Valiwe"</item>
+ <item msgid="4531508423703413340">"Vuliwe"</item>
+ </string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"Akutholakali"</item>
<item msgid="9103697205127645916">"Valiwe"</item>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index fc28f0976013..461a598e9341 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -23,7 +23,6 @@
<color name="system_bar_background_transparent">#00000000</color>
<color name="qs_tile_divider">#29ffffff</color><!-- 16% white -->
<color name="qs_detail_button_white">#B3FFFFFF</color><!-- 70% white -->
- <color name="qs_detail_transition">#66FFFFFF</color>
<color name="status_bar_clock_color">#FFFFFFFF</color>
<color name="qs_tile_disabled_color">#9E9E9E</color> <!-- 38% black -->
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 65f22b805d4e..fc2756ecc8e5 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -716,22 +716,6 @@
<!-- Flag to enable privacy dot views, it shall be true for normal case -->
<bool name="config_enablePrivacyDot">true</bool>
- <!-- The positions widgets can be in defined as View.Gravity constants -->
- <integer-array name="config_dreamComplicationPositions">
- </integer-array>
-
- <!-- Widget components to show as dream complications -->
- <string-array name="config_dreamAppWidgetComplications" translatable="false">
- </string-array>
-
- <!-- Width percentage of dream complications -->
- <item name="config_dreamComplicationWidthPercent" translatable="false" format="float"
- type="dimen">0.33</item>
-
- <!-- Height percentage of dream complications -->
- <item name="config_dreamComplicationHeightPercent" translatable="false" format="float"
- type="dimen">0.25</item>
-
<!-- Flag to enable dream overlay service and its registration -->
<bool name="config_dreamOverlayServiceEnabled">false</bool>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index b12db5d5f7c2..ceaacfc1271b 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -984,10 +984,11 @@
<!-- Media tap-to-transfer chip for sender device -->
<dimen name="media_ttt_chip_outer_padding">16dp</dimen>
<dimen name="media_ttt_text_size">16sp</dimen>
- <dimen name="media_ttt_icon_size">24dp</dimen>
- <dimen name="media_ttt_loading_size">20dp</dimen>
+ <dimen name="media_ttt_app_icon_size">24dp</dimen>
+ <dimen name="media_ttt_status_icon_size">20dp</dimen>
<dimen name="media_ttt_undo_button_vertical_padding">8dp</dimen>
<dimen name="media_ttt_undo_button_vertical_negative_margin">-8dp</dimen>
+ <dimen name="media_ttt_last_item_start_margin">12dp</dimen>
<!-- Media tap-to-transfer chip for receiver device -->
<dimen name="media_ttt_chip_size_receiver">100dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 08fb2c66e043..41d5735c8d80 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -670,6 +670,10 @@
<string name="quick_settings_dark_mode_secondary_label_on_at">On at <xliff:g id="time" example="10 pm">%s</xliff:g></string>
<!-- QuickSettings: Secondary text for when the Dark theme or some other tile will be on until some user-selected time. [CHAR LIMIT=20] -->
<string name="quick_settings_dark_mode_secondary_label_until">Until <xliff:g id="time" example="7 am">%s</xliff:g></string>
+ <!-- QuickSettings: Secondary text for when the Dark theme will be enabled at bedtime. [CHAR LIMIT=40] -->
+ <string name="quick_settings_dark_mode_secondary_label_on_at_bedtime">On at bedtime</string>
+ <!-- QuickSettings: Secondary text for when the Dark theme or some other tile will be on until bedtime ends. [CHAR LIMIT=40] -->
+ <string name="quick_settings_dark_mode_secondary_label_until_bedtime_ends">Until bedtime ends</string>
<!-- QuickSettings: NFC tile [CHAR LIMIT=NONE] -->
<string name="quick_settings_nfc_label">NFC</string>
@@ -2154,8 +2158,14 @@
<string name="media_transfer_undo">Undo</string>
<!-- Text to ask the user to move their device closer to a different device (deviceName) in order to play media on the different device. [CHAR LIMIT=75] -->
<string name="media_move_closer_to_start_cast">Move closer to play on <xliff:g id="deviceName" example="My Tablet">%1$s</xliff:g></string>
+ <!-- Text to ask the user to move their device closer to a different device (deviceName) in order to transfer media from the different device and back onto the current device. [CHAR LIMIT=75] -->
+ <string name="media_move_closer_to_end_cast">Move closer to <xliff:g id="deviceName" example="My Tablet">%1$s</xliff:g> to play here</string>
<!-- Text informing the user that their media is now playing on a different device (deviceName). [CHAR LIMIT=50] -->
- <string name="media_transfer_playing">Playing on <xliff:g id="deviceName" example="My Tablet">%1$s</xliff:g></string>
+ <string name="media_transfer_playing_different_device">Playing on <xliff:g id="deviceName" example="My Tablet">%1$s</xliff:g></string>
+ <!-- Text informing the user that their media is now playing on this device. [CHAR LIMIT=50] -->
+ <string name="media_transfer_playing_this_device">Playing on this phone</string>
+ <!-- Text informing the user that the media transfer has failed because something went wrong. [CHAR LIMIT=50] -->
+ <string name="media_transfer_failed">Something went wrong</string>
<!-- Error message indicating that a control timed out while waiting for an update [CHAR_LIMIT=30] -->
<string name="controls_error_timeout">Inactive, check app</string>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/IDeviceSenderCallback.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/IDeviceSenderCallback.aidl
deleted file mode 100644
index 484791df053e..000000000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/IDeviceSenderCallback.aidl
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.shared.mediattt;
-
-import android.media.MediaRoute2Info;
-import com.android.systemui.shared.mediattt.DeviceInfo;
-
-/**
- * A callback interface that can be invoked to trigger media transfer events on System UI.
- *
- * This interface is for the *sender* device, which is the device currently playing media. This
- * sender device can transfer the media to a different device, called the receiver.
- *
- * System UI will implement this interface and other services will invoke it.
- */
-interface IDeviceSenderCallback {
- /**
- * Invoke to notify System UI that this device (the sender) is close to a receiver device, so
- * the user can potentially *start* a cast to the receiver device if the user moves their device
- * a bit closer.
- *
- * Important notes:
- * - When this callback triggers, the device is close enough to inform the user that
- * transferring is an option, but the device is *not* close enough to actually initiate a
- * transfer yet.
- * - This callback is for *starting* a cast. It should be used when this device is currently
- * playing media locally and the media should be transferred to be played on the receiver
- * device instead.
- */
- oneway void closeToReceiverToStartCast(
- in MediaRoute2Info mediaInfo, in DeviceInfo otherDeviceInfo);
-}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/IDeviceSenderService.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/IDeviceSenderService.aidl
new file mode 100644
index 000000000000..eb1c9d058e20
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/IDeviceSenderService.aidl
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shared.mediattt;
+
+import android.media.MediaRoute2Info;
+import com.android.systemui.shared.mediattt.DeviceInfo;
+import com.android.systemui.shared.mediattt.IUndoTransferCallback;
+
+/**
+ * An interface that can be invoked to trigger media transfer events on System UI.
+ *
+ * This interface is for the *sender* device, which is the device currently playing media. This
+ * sender device can transfer the media to a different device, called the receiver.
+ *
+ * System UI will implement this interface and other services will invoke it.
+ */
+interface IDeviceSenderService {
+ /**
+ * Invoke to notify System UI that this device (the sender) is close to a receiver device, so
+ * the user can potentially *start* a cast to the receiver device if the user moves their device
+ * a bit closer.
+ *
+ * Important notes:
+ * - When this callback triggers, the device is close enough to inform the user that
+ * transferring is an option, but the device is *not* close enough to actually initiate a
+ * transfer yet.
+ * - This callback is for *starting* a cast. It should be used when this device is currently
+ * playing media locally and the media should be transferred to be played on the receiver
+ * device instead.
+ */
+ oneway void closeToReceiverToStartCast(
+ in MediaRoute2Info mediaInfo, in DeviceInfo otherDeviceInfo);
+
+ /**
+ * Invoke to notify System UI that this device (the sender) is close to a receiver device, so
+ * the user can potentially *end* a cast on the receiver device if the user moves this device a
+ * bit closer.
+ *
+ * Important notes:
+ * - When this callback triggers, the device is close enough to inform the user that
+ * transferring is an option, but the device is *not* close enough to actually initiate a
+ * transfer yet.
+ * - This callback is for *ending* a cast. It should be used when media is currently being
+ * played on the receiver device and the media should be transferred to play locally
+ * instead.
+ */
+ oneway void closeToReceiverToEndCast(
+ in MediaRoute2Info mediaInfo, in DeviceInfo otherDeviceInfo);
+
+ /**
+ * Invoke to notify System UI that a media transfer from this device (the sender) to a receiver
+ * device has been started.
+ *
+ * Important notes:
+ * - This callback is for *starting* a cast. It should be used when this device is currently
+ * playing media locally and the media has started being transferred to the receiver device
+ * instead.
+ */
+ oneway void transferToReceiverTriggered(
+ in MediaRoute2Info mediaInfo, in DeviceInfo otherDeviceInfo);
+
+ /**
+ * Invoke to notify System UI that a media transfer from the receiver and back to this device
+ * (the sender) has been started.
+ *
+ * Important notes:
+ * - This callback is for *ending* a cast. It should be used when media is currently being
+ * played on the receiver device and the media has started being transferred to play locally
+ * instead.
+ */
+ oneway void transferToThisDeviceTriggered(
+ in MediaRoute2Info mediaInfo, in DeviceInfo otherDeviceInfo);
+
+ /**
+ * Invoke to notify System UI that a media transfer from this device (the sender) to a receiver
+ * device has finished successfully.
+ *
+ * Important notes:
+ * - This callback is for *starting* a cast. It should be used when this device had previously
+ * been playing media locally and the media has successfully been transferred to the
+ * receiver device instead.
+ *
+ * @param undoCallback will be invoked if the user chooses to undo this transfer.
+ */
+ oneway void transferToReceiverSucceeded(
+ in MediaRoute2Info mediaInfo,
+ in DeviceInfo otherDeviceInfo,
+ in IUndoTransferCallback undoCallback);
+
+ /**
+ * Invoke to notify System UI that a media transfer from the receiver and back to this device
+ * (the sender) has finished successfully.
+ *
+ * Important notes:
+ * - This callback is for *ending* a cast. It should be used when media was previously being
+ * played on the receiver device and has been successfully transferred to play locally on
+ * this device instead.
+ *
+ * @param undoCallback will be invoked if the user chooses to undo this transfer.
+ */
+ oneway void transferToThisDeviceSucceeded(
+ in MediaRoute2Info mediaInfo,
+ in DeviceInfo otherDeviceInfo,
+ in IUndoTransferCallback undoCallback);
+
+ /**
+ * Invoke to notify System UI that the attempted transfer has failed.
+ *
+ * This callback will be used for both the transfer that should've *started* playing the media
+ * on the receiver and the transfer that should've *ended* the playing on the receiver.
+ */
+ oneway void transferFailed(in MediaRoute2Info mediaInfo, in DeviceInfo otherDeviceInfo);
+
+ /**
+ * Invoke to notify System UI that this device is no longer close to the receiver device.
+ */
+ oneway void noLongerCloseToReceiver(
+ in MediaRoute2Info mediaInfo, in DeviceInfo otherDeviceInfo);
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/IUndoTransferCallback.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/IUndoTransferCallback.aidl
new file mode 100644
index 000000000000..b47be8736d23
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/IUndoTransferCallback.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shared.mediattt;
+
+/**
+ * An interface that will be invoked by System UI if the user choose to undo a transfer.
+ *
+ * Other services will implement this interface and System UI will invoke it.
+ */
+interface IUndoTransferCallback {
+
+ /**
+ * Invoked by SystemUI when the user requests to undo the media transfer that just occurred.
+ *
+ * Implementors of this method are repsonsible for actually undoing the transfer.
+ */
+ oneway void onUndoTriggered();
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/navigationbar/RegionSamplingHelper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/navigationbar/RegionSamplingHelper.java
index 1d2caf9ab545..6345d113faed 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/navigationbar/RegionSamplingHelper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/navigationbar/RegionSamplingHelper.java
@@ -275,23 +275,27 @@ public class RegionSamplingHelper implements View.OnAttachStateChangeListener,
}
public void dump(PrintWriter pw) {
- pw.println("RegionSamplingHelper:");
- pw.println(" sampleView isAttached: " + mSampledView.isAttachedToWindow());
- pw.println(" sampleView isScValid: " + (mSampledView.isAttachedToWindow()
+ dump("", pw);
+ }
+
+ public void dump(String prefix, PrintWriter pw) {
+ pw.println(prefix + "RegionSamplingHelper:");
+ pw.println(prefix + "\tsampleView isAttached: " + mSampledView.isAttachedToWindow());
+ pw.println(prefix + "\tsampleView isScValid: " + (mSampledView.isAttachedToWindow()
? mSampledView.getViewRootImpl().getSurfaceControl().isValid()
: "notAttached"));
- pw.println(" mSamplingEnabled: " + mSamplingEnabled);
- pw.println(" mSamplingListenerRegistered: " + mSamplingListenerRegistered);
- pw.println(" mSamplingRequestBounds: " + mSamplingRequestBounds);
- pw.println(" mRegisteredSamplingBounds: " + mRegisteredSamplingBounds);
- pw.println(" mLastMedianLuma: " + mLastMedianLuma);
- pw.println(" mCurrentMedianLuma: " + mCurrentMedianLuma);
- pw.println(" mWindowVisible: " + mWindowVisible);
- pw.println(" mWindowHasBlurs: " + mWindowHasBlurs);
- pw.println(" mWaitingOnDraw: " + mWaitingOnDraw);
- pw.println(" mRegisteredStopLayer: " + mRegisteredStopLayer);
- pw.println(" mWrappedStopLayer: " + mWrappedStopLayer);
- pw.println(" mIsDestroyed: " + mIsDestroyed);
+ pw.println(prefix + "\tmSamplingEnabled: " + mSamplingEnabled);
+ pw.println(prefix + "\tmSamplingListenerRegistered: " + mSamplingListenerRegistered);
+ pw.println(prefix + "\tmSamplingRequestBounds: " + mSamplingRequestBounds);
+ pw.println(prefix + "\tmRegisteredSamplingBounds: " + mRegisteredSamplingBounds);
+ pw.println(prefix + "\tmLastMedianLuma: " + mLastMedianLuma);
+ pw.println(prefix + "\tmCurrentMedianLuma: " + mCurrentMedianLuma);
+ pw.println(prefix + "\tmWindowVisible: " + mWindowVisible);
+ pw.println(prefix + "\tmWindowHasBlurs: " + mWindowHasBlurs);
+ pw.println(prefix + "\tmWaitingOnDraw: " + mWaitingOnDraw);
+ pw.println(prefix + "\tmRegisteredStopLayer: " + mRegisteredStopLayer);
+ pw.println(prefix + "\tmWrappedStopLayer: " + mWrappedStopLayer);
+ pw.println(prefix + "\tmIsDestroyed: " + mIsDestroyed);
}
public interface SamplingCallback {
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
index 605d37628ec7..bb7a0a719a74 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
@@ -16,6 +16,7 @@
package com.android.systemui.shared.rotation;
+import static android.content.pm.PackageManager.FEATURE_PC;
import static android.view.Display.DEFAULT_DISPLAY;
import static com.android.internal.view.RotationPolicy.NATURAL_ROTATION;
@@ -51,13 +52,14 @@ import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.UiEventLoggerImpl;
import com.android.internal.view.RotationPolicy;
-import com.android.systemui.shared.rotation.RotationButton.RotationButtonUpdatesCallback;
import com.android.systemui.shared.recents.utilities.Utilities;
import com.android.systemui.shared.recents.utilities.ViewRippler;
+import com.android.systemui.shared.rotation.RotationButton.RotationButtonUpdatesCallback;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.TaskStackChangeListeners;
+import java.io.PrintWriter;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Supplier;
@@ -200,7 +202,7 @@ public class RotationButtonController {
}
public void registerListeners() {
- if (mListenersRegistered) {
+ if (mListenersRegistered || getContext().getPackageManager().hasSystemFeature(FEATURE_PC)) {
return;
}
@@ -414,6 +416,9 @@ public class RotationButtonController {
}
public void onTaskbarStateChange(boolean visible, boolean stashed) {
+ if (getRotationButton() == null) {
+ return;
+ }
getRotationButton().onTaskbarStateChanged(visible, stashed);
}
@@ -446,6 +451,30 @@ public class RotationButtonController {
return mDarkIconColor;
}
+ public void dumpLogs(String prefix, PrintWriter pw) {
+ pw.println(prefix + "RotationButtonController:");
+
+ pw.println(String.format(
+ "%s\tmIsRecentsAnimationRunning=%b", prefix, mIsRecentsAnimationRunning));
+ pw.println(String.format("%s\tmHomeRotationEnabled=%b", prefix, mHomeRotationEnabled));
+ pw.println(String.format(
+ "%s\tmLastRotationSuggestion=%d", prefix, mLastRotationSuggestion));
+ pw.println(String.format(
+ "%s\tmPendingRotationSuggestion=%b", prefix, mPendingRotationSuggestion));
+ pw.println(String.format(
+ "%s\tmHoveringRotationSuggestion=%b", prefix, mHoveringRotationSuggestion));
+ pw.println(String.format("%s\tmListenersRegistered=%b", prefix, mListenersRegistered));
+ pw.println(String.format(
+ "%s\tmIsNavigationBarShowing=%b", prefix, mIsNavigationBarShowing));
+ pw.println(String.format("%s\tmBehavior=%d", prefix, mBehavior));
+ pw.println(String.format(
+ "%s\tmSkipOverrideUserLockPrefsOnce=%b", prefix, mSkipOverrideUserLockPrefsOnce));
+ pw.println(String.format(
+ "%s\tmLightIconColor=0x%s", prefix, Integer.toHexString(mLightIconColor)));
+ pw.println(String.format(
+ "%s\tmDarkIconColor=0x%s", prefix, Integer.toHexString(mDarkIconColor)));
+ }
+
public RotationButton getRotationButton() {
return mRotationButton;
}
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 54c798c7d95d..5d092d02a835 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
@@ -55,8 +55,8 @@ public class QuickStepContract {
// See IStartingWindow.aidl
public static final String KEY_EXTRA_SHELL_STARTING_WINDOW =
"extra_shell_starting_window";
- // See ISmartspaceTransitionController.aidl
- public static final String KEY_EXTRA_SMARTSPACE_TRANSITION_CONTROLLER = "smartspace_transition";
+ // See ISysuiUnlockAnimationController.aidl
+ public static final String KEY_EXTRA_UNLOCK_ANIMATION_CONTROLLER = "unlock_animation";
// See IRecentTasks.aidl
public static final String KEY_EXTRA_RECENT_TASKS = "recent_tasks";
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 2ae32c71269a..f2f382dea595 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
@@ -25,6 +25,8 @@ 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 static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_RECENTS;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
@@ -145,6 +147,11 @@ public class RemoteTransitionCompat implements Parcelable {
&& taskInfo.pictureInPictureParams.isAutoEnterEnabled()) {
pipTask = taskInfo.token;
}
+ } else if (change.getTaskInfo() != null
+ && change.getTaskInfo().topActivityType == ACTIVITY_TYPE_RECENTS) {
+ // This task is for recents, keep it on top.
+ t.setLayer(leashMap.get(change.getLeash()),
+ info.getChanges().size() * 3 - i);
}
}
// Also make all the wallpapers opaque since we want the visible from the start
@@ -310,53 +317,48 @@ public class RemoteTransitionCompat implements Parcelable {
return;
}
if (mWrapped != null) mWrapped.finish(toHome, sendUserLeaveHint);
- try {
- if (!toHome && mPausingTasks != null && mOpeningLeashes == 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 (and also
- // re-showing it's task).
- final WindowContainerTransaction wct = new WindowContainerTransaction();
- final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
- for (int i = mPausingTasks.size() - 1; i >= 0; --i) {
- // reverse order so that index 0 ends up on top
- wct.reorder(mPausingTasks.get(i), true /* onTop */);
- t.show(mInfo.getChange(mPausingTasks.get(i)).getLeash());
- }
- mFinishCB.onTransitionFinished(wct, t);
- } else {
- if (mOpeningLeashes != null) {
- // TODO: the launcher animation should handle this
- final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
- for (int i = 0; i < mOpeningLeashes.size(); ++i) {
- t.show(mOpeningLeashes.get(i));
- t.setAlpha(mOpeningLeashes.get(i), 1.f);
- }
- t.apply();
- }
- 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 */);
+ final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ final WindowContainerTransaction wct;
+
+ if (!toHome && mPausingTasks != null && mOpeningLeashes == 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 (and also
+ // re-showing it's task).
+ wct = new WindowContainerTransaction();
+ for (int i = mPausingTasks.size() - 1; i >= 0; --i) {
+ // reverse order so that index 0 ends up on top
+ wct.reorder(mPausingTasks.get(i), true /* onTop */);
+ t.show(mInfo.getChange(mPausingTasks.get(i)).getLeash());
+ }
+ } else {
+ wct = null;
+ if (mOpeningLeashes != null) {
+ // TODO: the launcher animation should handle this
+ for (int i = 0; i < mOpeningLeashes.size(); ++i) {
+ t.show(mOpeningLeashes.get(i));
+ t.setAlpha(mOpeningLeashes.get(i), 1.f);
}
-
}
- } catch (RemoteException e) {
- Log.e("RemoteTransitionCompat", "Failed to call animation finish callback", e);
+ if (mPipTask != null && mPipTransaction != null) {
+ t.show(mInfo.getChange(mPipTask).getLeash());
+ PictureInPictureSurfaceTransaction.apply(mPipTransaction,
+ mInfo.getChange(mPipTask).getLeash(), t);
+ mPipTask = null;
+ mPipTransaction = null;
+ }
}
// Release surface references now. This is apparently to free GPU
// memory while doing quick operations (eg. during CTS).
- SurfaceControl.Transaction t = new SurfaceControl.Transaction();
for (int i = 0; i < mLeashMap.size(); ++i) {
if (mLeashMap.keyAt(i) == mLeashMap.valueAt(i)) continue;
t.remove(mLeashMap.valueAt(i));
}
- t.apply();
+ try {
+ mFinishCB.onTransitionFinished(wct, t);
+ } catch (RemoteException e) {
+ Log.e("RemoteTransitionCompat", "Failed to call animation finish callback", e);
+ t.apply();
+ }
for (int i = 0; i < mInfo.getChanges().size(); ++i) {
mInfo.getChanges().get(i).getLeash().release();
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/ILauncherUnlockAnimationController.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/ILauncherUnlockAnimationController.aidl
new file mode 100644
index 000000000000..366193c2cc41
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/ILauncherUnlockAnimationController.aidl
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shared.system.smartspace;
+
+import com.android.systemui.shared.system.smartspace.SmartspaceState;
+
+// Methods for System UI to interface with Launcher to perform the unlock animation.
+interface ILauncherUnlockAnimationController {
+ // Prepares Launcher for the unlock animation by setting scale/alpha/etc. to their starting
+ // values.
+ void prepareForUnlock(boolean willAnimateSmartspace, int selectedPage);
+
+ // Set the unlock percentage. This is used when System UI is controlling each frame of the
+ // unlock animation, such as during a swipe to unlock touch gesture.
+ oneway void setUnlockAmount(float amount);
+
+ // Play a full unlock animation from 0f to 1f. This is used when System UI is unlocking from a
+ // single action, such as biometric auth, and doesn't need to control individual frames.
+ oneway void playUnlockAnimation(boolean unlocked, long duration);
+
+ // Set the selected page on Launcher's smartspace.
+ oneway void setSmartspaceSelectedPage(int selectedPage);
+
+ // Set the visibility of Launcher's smartspace.
+ void setSmartspaceVisibility(int visibility);
+
+ // Tell SystemUI the smartspace's current state. Launcher code should call this whenever the
+ // smartspace state may have changed.
+ oneway void dispatchSmartspaceStateToSysui();
+} \ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/ISmartspaceCallback.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/ISmartspaceCallback.aidl
deleted file mode 100644
index 511df4c285b4..000000000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/ISmartspaceCallback.aidl
+++ /dev/null
@@ -1,34 +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.shared.system.smartspace;
-
-import com.android.systemui.shared.system.smartspace.SmartspaceState;
-
-// Methods for getting and setting the state of a SmartSpace. This is used to allow a remote process
-// (such as System UI) to sync with and control a SmartSpace view hosted in another process (such as
-// Launcher).
-interface ISmartspaceCallback {
-
- // Return information about the state of the SmartSpace, including location on-screen and
- // currently selected page.
- SmartspaceState getSmartspaceState();
-
- // Set the currently selected page of this SmartSpace.
- oneway void setSelectedPage(int selectedPage);
-
- oneway void setVisibility(int visibility);
-} \ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/ISysuiUnlockAnimationController.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/ISysuiUnlockAnimationController.aidl
new file mode 100644
index 000000000000..cf83f62af550
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/ISysuiUnlockAnimationController.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 com.android.systemui.shared.system.smartspace;
+
+import com.android.systemui.shared.system.smartspace.ILauncherUnlockAnimationController;
+import com.android.systemui.shared.system.smartspace.SmartspaceState;
+
+// System UI unlock controller. Launcher will provide a LauncherUnlockAnimationController to this
+// controller, which System UI will use to control the unlock animation within the Launcher window.
+interface ISysuiUnlockAnimationController {
+ // Provides an implementation of the LauncherUnlockAnimationController to System UI, so that
+ // SysUI can use it to control the unlock animation in the launcher window.
+ oneway void setLauncherUnlockController(ILauncherUnlockAnimationController callback);
+
+ // Called by Launcher whenever anything happens to change the state of its smartspace. System UI
+ // proactively saves this and uses it to perform the unlock animation without needing to make a
+ // blocking query to Launcher asking about the smartspace state.
+ oneway void onLauncherSmartspaceStateUpdated(in SmartspaceState state);
+} \ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/SmartspaceState.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/SmartspaceState.kt
index 2d51c4d13611..d7e61d60aa55 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/SmartspaceState.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/SmartspaceState.kt
@@ -28,15 +28,18 @@ import android.os.Parcelable
class SmartspaceState() : Parcelable {
var boundsOnScreen: Rect = Rect()
var selectedPage = 0
+ var visibleOnScreen = false
constructor(parcel: Parcel) : this() {
this.boundsOnScreen = parcel.readParcelable(Rect::javaClass.javaClass.classLoader)
this.selectedPage = parcel.readInt()
+ this.visibleOnScreen = parcel.readBoolean()
}
override fun writeToParcel(dest: Parcel?, flags: Int) {
dest?.writeParcelable(boundsOnScreen, 0)
dest?.writeInt(selectedPage)
+ dest?.writeBoolean(visibleOnScreen)
}
override fun describeContents(): Int {
@@ -44,7 +47,9 @@ class SmartspaceState() : Parcelable {
}
override fun toString(): String {
- return "boundsOnScreen: $boundsOnScreen, selectedPage: $selectedPage"
+ return "boundsOnScreen: $boundsOnScreen, " +
+ "selectedPage: $selectedPage, " +
+ "visibleOnScreen: $visibleOnScreen"
}
companion object CREATOR : Parcelable.Creator<SmartspaceState> {
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionProgressProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionProgressProvider.kt
index e17f43e64b21..409dc95ab131 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionProgressProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionProgressProvider.kt
@@ -16,12 +16,14 @@
package com.android.systemui.unfold
import android.annotation.FloatRange
-import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
import com.android.systemui.statusbar.policy.CallbackController
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
/**
* Interface that allows to receive unfold transition progress updates.
+ *
* It can be used to update view properties based on the current animation progress.
+ *
* onTransitionProgress callback could be called on each frame.
*
* Use [createUnfoldTransitionProgressProvider] to create instances of this interface
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfig.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfig.kt
index 3f027e30b473..d1b06394b818 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfig.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfig.kt
@@ -18,9 +18,8 @@ package com.android.systemui.unfold.config
import android.content.Context
import android.os.SystemProperties
-internal class ResourceUnfoldTransitionConfig(
- private val context: Context
-) : UnfoldTransitionConfig {
+internal class ResourceUnfoldTransitionConfig(private val context: Context) :
+ UnfoldTransitionConfig {
override val isEnabled: Boolean
get() = readIsEnabledResource() && isPropertyEnabled
@@ -29,19 +28,22 @@ internal class ResourceUnfoldTransitionConfig(
get() = readIsHingeAngleEnabled()
private val isPropertyEnabled: Boolean
- get() = SystemProperties.getInt(UNFOLD_TRANSITION_MODE_PROPERTY_NAME,
- UNFOLD_TRANSITION_PROPERTY_ENABLED) == UNFOLD_TRANSITION_PROPERTY_ENABLED
+ get() =
+ SystemProperties.getInt(
+ UNFOLD_TRANSITION_MODE_PROPERTY_NAME, UNFOLD_TRANSITION_PROPERTY_ENABLED) ==
+ UNFOLD_TRANSITION_PROPERTY_ENABLED
- private fun readIsEnabledResource(): Boolean = context.resources
- .getBoolean(com.android.internal.R.bool.config_unfoldTransitionEnabled)
+ private fun readIsEnabledResource(): Boolean =
+ context.resources.getBoolean(com.android.internal.R.bool.config_unfoldTransitionEnabled)
- private fun readIsHingeAngleEnabled(): Boolean = context.resources
- .getBoolean(com.android.internal.R.bool.config_unfoldTransitionHingeAngle)
+ private fun readIsHingeAngleEnabled(): Boolean =
+ context.resources.getBoolean(com.android.internal.R.bool.config_unfoldTransitionHingeAngle)
}
/**
- * Temporary persistent property to control unfold transition mode
- * See [com.android.unfold.config.AnimationMode]
+ * Temporary persistent property to control unfold transition mode.
+ *
+ * See [com.android.unfold.config.AnimationMode].
*/
private const val UNFOLD_TRANSITION_MODE_PROPERTY_NAME = "persist.unfold.transition_enabled"
private const val UNFOLD_TRANSITION_PROPERTY_ENABLED = 1
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/FixedTimingTransitionProgressProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/FixedTimingTransitionProgressProvider.kt
index 732882e99038..4c85b055aeae 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/FixedTimingTransitionProgressProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/FixedTimingTransitionProgressProvider.kt
@@ -25,21 +25,17 @@ import com.android.systemui.unfold.updates.FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE
import com.android.systemui.unfold.updates.FoldStateProvider
import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdate
-/**
- * Emits animation progress with fixed timing after unfolding
- */
+/** Emits animation progress with fixed timing after unfolding */
internal class FixedTimingTransitionProgressProvider(
private val foldStateProvider: FoldStateProvider
) : UnfoldTransitionProgressProvider, FoldStateProvider.FoldUpdatesListener {
private val animatorListener = AnimatorListener()
private val animator =
- ObjectAnimator.ofFloat(this, AnimationProgressProperty, 0f, 1f)
- .apply {
- duration = TRANSITION_TIME_MILLIS
- addListener(animatorListener)
- }
-
+ ObjectAnimator.ofFloat(this, AnimationProgressProperty, 0f, 1f).apply {
+ duration = TRANSITION_TIME_MILLIS
+ addListener(animatorListener)
+ }
private var transitionProgress: Float = 0.0f
set(value) {
@@ -62,10 +58,8 @@ internal class FixedTimingTransitionProgressProvider(
override fun onFoldUpdate(@FoldUpdate update: Int) {
when (update) {
- FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE ->
- animator.start()
- FOLD_UPDATE_FINISH_CLOSED ->
- animator.cancel()
+ FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE -> animator.start()
+ FOLD_UPDATE_FINISH_CLOSED -> animator.cancel()
}
}
@@ -77,16 +71,12 @@ internal class FixedTimingTransitionProgressProvider(
listeners.remove(listener)
}
- override fun onHingeAngleUpdate(angle: Float) {
- }
+ override fun onHingeAngleUpdate(angle: Float) {}
private object AnimationProgressProperty :
FloatProperty<FixedTimingTransitionProgressProvider>("animation_progress") {
- override fun setValue(
- provider: FixedTimingTransitionProgressProvider,
- value: Float
- ) {
+ override fun setValue(provider: FixedTimingTransitionProgressProvider, value: Float) {
provider.transitionProgress = value
}
@@ -104,11 +94,9 @@ internal class FixedTimingTransitionProgressProvider(
listeners.forEach { it.onTransitionFinished() }
}
- override fun onAnimationRepeat(animator: Animator) {
- }
+ override fun onAnimationRepeat(animator: Animator) {}
- override fun onAnimationCancel(animator: Animator) {
- }
+ override fun onAnimationCancel(animator: Animator) {}
}
private companion object {
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt
index a701b44cf916..5266f096e12c 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt
@@ -23,10 +23,10 @@ import androidx.dynamicanimation.animation.SpringAnimation
import androidx.dynamicanimation.animation.SpringForce
import com.android.systemui.unfold.UnfoldTransitionProgressProvider
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
-import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_HALF_OPEN
import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_CLOSED
-import com.android.systemui.unfold.updates.FOLD_UPDATE_START_CLOSING
import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_FULL_OPEN
+import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_HALF_OPEN
+import com.android.systemui.unfold.updates.FOLD_UPDATE_START_CLOSING
import com.android.systemui.unfold.updates.FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE
import com.android.systemui.unfold.updates.FoldStateProvider
import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdate
@@ -35,13 +35,10 @@ import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdatesListener
/** Maps fold updates to unfold transition progress using DynamicAnimation. */
internal class PhysicsBasedUnfoldTransitionProgressProvider(
private val foldStateProvider: FoldStateProvider
-) :
- UnfoldTransitionProgressProvider,
- FoldUpdatesListener,
- DynamicAnimation.OnAnimationEndListener {
+) : UnfoldTransitionProgressProvider, FoldUpdatesListener, DynamicAnimation.OnAnimationEndListener {
- private val springAnimation = SpringAnimation(this, AnimationProgressProperty)
- .apply {
+ private val springAnimation =
+ SpringAnimation(this, AnimationProgressProperty).apply {
addEndListener(this@PhysicsBasedUnfoldTransitionProgressProvider)
}
@@ -121,9 +118,7 @@ internal class PhysicsBasedUnfoldTransitionProgressProvider(
isTransitionRunning = false
springAnimation.cancel()
- listeners.forEach {
- it.onTransitionFinished()
- }
+ listeners.forEach { it.onTransitionFinished() }
if (DEBUG) {
Log.d(TAG, "onTransitionFinished")
@@ -143,9 +138,7 @@ internal class PhysicsBasedUnfoldTransitionProgressProvider(
}
private fun onStartTransition() {
- listeners.forEach {
- it.onTransitionStarted()
- }
+ listeners.forEach { it.onTransitionStarted() }
isTransitionRunning = true
if (DEBUG) {
@@ -157,11 +150,12 @@ internal class PhysicsBasedUnfoldTransitionProgressProvider(
if (!isTransitionRunning) onStartTransition()
springAnimation.apply {
- spring = SpringForce().apply {
- finalPosition = startValue
- dampingRatio = SpringForce.DAMPING_RATIO_NO_BOUNCY
- stiffness = SPRING_STIFFNESS
- }
+ spring =
+ SpringForce().apply {
+ finalPosition = startValue
+ dampingRatio = SpringForce.DAMPING_RATIO_NO_BOUNCY
+ stiffness = SPRING_STIFFNESS
+ }
minimumVisibleChange = MINIMAL_VISIBLE_CHANGE
setStartValue(startValue)
setMinValue(0f)
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
index 204ae09b4852..24ecf8781a18 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
@@ -45,11 +45,9 @@ constructor(
private val outputListeners: MutableList<FoldUpdatesListener> = mutableListOf()
- @FoldUpdate
- private var lastFoldUpdate: Int? = null
+ @FoldUpdate private var lastFoldUpdate: Int? = null
- @FloatRange(from = 0.0, to = 180.0)
- private var lastHingeAngle: Float = 0f
+ @FloatRange(from = 0.0, to = 180.0) private var lastHingeAngle: Float = 0f
private val hingeAngleListener = HingeAngleListener()
private val screenListener = ScreenStatusListener()
@@ -60,10 +58,7 @@ constructor(
private var isUnfoldHandled = true
override fun start() {
- deviceStateManager.registerCallback(
- mainExecutor,
- foldStateListener
- )
+ deviceStateManager.registerCallback(mainExecutor, foldStateListener)
screenStatusProvider.addCallback(screenListener)
hingeAngleProvider.addCallback(hingeAngleListener)
}
@@ -87,11 +82,14 @@ constructor(
get() = !isFolded && lastFoldUpdate == FOLD_UPDATE_FINISH_FULL_OPEN
private val isTransitionInProgess: Boolean
- get() = lastFoldUpdate == FOLD_UPDATE_START_OPENING ||
+ get() =
+ lastFoldUpdate == FOLD_UPDATE_START_OPENING ||
lastFoldUpdate == FOLD_UPDATE_START_CLOSING
private fun onHingeAngle(angle: Float) {
- if (DEBUG) { Log.d(TAG, "Hinge angle: $angle, lastHingeAngle: $lastHingeAngle") }
+ if (DEBUG) {
+ Log.d(TAG, "Hinge angle: $angle, lastHingeAngle: $lastHingeAngle")
+ }
val isClosing = angle < lastHingeAngle
val isFullyOpened = FULLY_OPEN_DEGREES - angle < FULLY_OPEN_THRESHOLD_DEGREES
@@ -116,24 +114,28 @@ constructor(
}
private inner class FoldStateListener(context: Context) :
- DeviceStateManager.FoldStateListener(context, { folded: Boolean ->
- isFolded = folded
- lastHingeAngle = FULLY_CLOSED_DEGREES
-
- if (folded) {
- hingeAngleProvider.stop()
- notifyFoldUpdate(FOLD_UPDATE_FINISH_CLOSED)
- cancelTimeout()
- isUnfoldHandled = false
- } else {
- notifyFoldUpdate(FOLD_UPDATE_START_OPENING)
- rescheduleAbortAnimationTimeout()
- hingeAngleProvider.start()
- }
- })
+ DeviceStateManager.FoldStateListener(
+ context,
+ { folded: Boolean ->
+ isFolded = folded
+ lastHingeAngle = FULLY_CLOSED_DEGREES
+
+ if (folded) {
+ hingeAngleProvider.stop()
+ notifyFoldUpdate(FOLD_UPDATE_FINISH_CLOSED)
+ cancelTimeout()
+ isUnfoldHandled = false
+ } else {
+ notifyFoldUpdate(FOLD_UPDATE_START_OPENING)
+ rescheduleAbortAnimationTimeout()
+ hingeAngleProvider.start()
+ }
+ })
private fun notifyFoldUpdate(@FoldUpdate update: Int) {
- if (DEBUG) { Log.d(TAG, stateToString(update)) }
+ if (DEBUG) {
+ Log.d(TAG, stateToString(update))
+ }
outputListeners.forEach { it.onFoldUpdate(update) }
lastFoldUpdate = update
}
@@ -149,8 +151,7 @@ constructor(
handler.removeCallbacks(timeoutRunnable)
}
- private inner class ScreenStatusListener :
- ScreenStatusProvider.ScreenListener {
+ private inner class ScreenStatusListener : ScreenStatusProvider.ScreenListener {
override fun onScreenTurnedOn() {
// Trigger this event only if we are unfolded and this is the first screen
@@ -198,9 +199,7 @@ private const val DEBUG = false
* Time after which [FOLD_UPDATE_FINISH_HALF_OPEN] is emitted following a
* [FOLD_UPDATE_START_CLOSING] or [FOLD_UPDATE_START_OPENING] event, if an end state is not reached.
*/
-@VisibleForTesting
-const val HALF_OPENED_TIMEOUT_MILLIS = 1000L
+@VisibleForTesting const val HALF_OPENED_TIMEOUT_MILLIS = 1000L
/** Threshold after which we consider the device fully unfolded. */
-@VisibleForTesting
-const val FULLY_OPEN_THRESHOLD_DEGREES = 15f \ No newline at end of file
+@VisibleForTesting const val FULLY_OPEN_THRESHOLD_DEGREES = 15f
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/FoldStateProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/FoldStateProvider.kt
index df3563df5fc6..5495316cd5b2 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/FoldStateProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/FoldStateProvider.kt
@@ -17,8 +17,8 @@ package com.android.systemui.unfold.updates
import android.annotation.FloatRange
import android.annotation.IntDef
-import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdatesListener
import com.android.systemui.statusbar.policy.CallbackController
+import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdatesListener
/**
* Allows to subscribe to main events related to fold/unfold process such as hinge angle update,
@@ -35,14 +35,16 @@ interface FoldStateProvider : CallbackController<FoldUpdatesListener> {
fun onFoldUpdate(@FoldUpdate update: Int)
}
- @IntDef(prefix = ["FOLD_UPDATE_"], value = [
- FOLD_UPDATE_START_OPENING,
- FOLD_UPDATE_START_CLOSING,
- FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE,
- FOLD_UPDATE_FINISH_HALF_OPEN,
- FOLD_UPDATE_FINISH_FULL_OPEN,
- FOLD_UPDATE_FINISH_CLOSED
- ])
+ @IntDef(
+ prefix = ["FOLD_UPDATE_"],
+ value =
+ [
+ FOLD_UPDATE_START_OPENING,
+ FOLD_UPDATE_START_CLOSING,
+ FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE,
+ FOLD_UPDATE_FINISH_HALF_OPEN,
+ FOLD_UPDATE_FINISH_FULL_OPEN,
+ FOLD_UPDATE_FINISH_CLOSED])
@Retention(AnnotationRetention.SOURCE)
annotation class FoldUpdate
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/EmptyHingeAngleProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/EmptyHingeAngleProvider.kt
index 4ca1a531fc25..b351585de364 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/EmptyHingeAngleProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/EmptyHingeAngleProvider.kt
@@ -3,15 +3,11 @@ package com.android.systemui.unfold.updates.hinge
import androidx.core.util.Consumer
internal object EmptyHingeAngleProvider : HingeAngleProvider {
- override fun start() {
- }
+ override fun start() {}
- override fun stop() {
- }
+ override fun stop() {}
- override fun removeCallback(listener: Consumer<Float>) {
- }
+ override fun removeCallback(listener: Consumer<Float>) {}
- override fun addCallback(listener: Consumer<Float>) {
- }
+ override fun addCallback(listener: Consumer<Float>) {}
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/HingeAngleProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/HingeAngleProvider.kt
index 6f524560de99..48a5b12c759a 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/HingeAngleProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/HingeAngleProvider.kt
@@ -5,9 +5,10 @@ import com.android.systemui.statusbar.policy.CallbackController
/**
* Emits device hinge angle values (angle between two integral parts of the device).
- * The hinge angle could be from 0 to 360 degrees inclusive.
- * For foldable devices usually 0 corresponds to fully closed (folded) state and
- * 180 degrees corresponds to fully open (flat) state
+ *
+ * The hinge angle could be from 0 to 360 degrees inclusive. For foldable devices usually 0
+ * corresponds to fully closed (folded) state and 180 degrees corresponds to fully open (flat)
+ * state.
*/
interface HingeAngleProvider : CallbackController<Consumer<Float>> {
fun start()
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/HingeSensorAngleProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/HingeSensorAngleProvider.kt
index a42ebef04de1..f6fe1ede9ce0 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/HingeSensorAngleProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/HingeSensorAngleProvider.kt
@@ -6,9 +6,8 @@ import android.hardware.SensorEventListener
import android.hardware.SensorManager
import androidx.core.util.Consumer
-internal class HingeSensorAngleProvider(
- private val sensorManager: SensorManager
-) : HingeAngleProvider {
+internal class HingeSensorAngleProvider(private val sensorManager: SensorManager) :
+ HingeAngleProvider {
private val sensorListener = HingeAngleSensorListener()
private val listeners: MutableList<Consumer<Float>> = arrayListOf()
@@ -32,8 +31,7 @@ internal class HingeSensorAngleProvider(
private inner class HingeAngleSensorListener : SensorEventListener {
- override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {
- }
+ override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {}
override fun onSensorChanged(event: SensorEvent) {
listeners.forEach { it.accept(event.values[0]) }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/screen/ScreenStatusProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/screen/ScreenStatusProvider.kt
index 1eec8033ac5d..668c69442cac 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/screen/ScreenStatusProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/screen/ScreenStatusProvider.kt
@@ -15,8 +15,8 @@
*/
package com.android.systemui.unfold.updates.screen
-import com.android.systemui.unfold.updates.screen.ScreenStatusProvider.ScreenListener
import com.android.systemui.statusbar.policy.CallbackController
+import com.android.systemui.unfold.updates.screen.ScreenStatusProvider.ScreenListener
interface ScreenStatusProvider : CallbackController<ScreenListener> {
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProvider.kt
index 58d7dfb133a5..53c528ff24a8 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProvider.kt
@@ -10,9 +10,8 @@ import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionPr
/**
* [UnfoldTransitionProgressProvider] that emits transition progress only when the display has
- * default rotation or 180 degrees opposite rotation (ROTATION_0 or ROTATION_180).
- * It could be helpful to run the animation only when the display's rotation is perpendicular
- * to the fold.
+ * default rotation or 180 degrees opposite rotation (ROTATION_0 or ROTATION_180). It could be
+ * helpful to run the animation only when the display's rotation is perpendicular to the fold.
*/
class NaturalRotationUnfoldProgressProvider(
private val context: Context,
@@ -21,7 +20,7 @@ class NaturalRotationUnfoldProgressProvider(
) : UnfoldTransitionProgressProvider {
private val scopedUnfoldTransitionProgressProvider =
- ScopedUnfoldTransitionProgressProvider(unfoldTransitionProgressProvider)
+ ScopedUnfoldTransitionProgressProvider(unfoldTransitionProgressProvider)
private val rotationWatcher = RotationWatcher()
private var isNaturalRotation: Boolean = false
@@ -37,8 +36,8 @@ class NaturalRotationUnfoldProgressProvider(
}
private fun onRotationChanged(rotation: Int) {
- val isNewRotationNatural = rotation == Surface.ROTATION_0 ||
- rotation == Surface.ROTATION_180
+ val isNewRotationNatural =
+ rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180
if (isNaturalRotation != isNewRotationNatural) {
isNaturalRotation = isNewRotationNatural
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt
index ee79b8761059..dfe87921dd42 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt
@@ -21,17 +21,18 @@ constructor(
private val scopedUnfoldTransitionProgressProvider =
ScopedUnfoldTransitionProgressProvider(progressProviderToWrap)
- private val animatorDurationScaleObserver = object : ContentObserver(null) {
- override fun onChange(selfChange: Boolean) {
- onAnimatorScaleChanged()
+ private val animatorDurationScaleObserver =
+ object : ContentObserver(null) {
+ override fun onChange(selfChange: Boolean) {
+ onAnimatorScaleChanged()
+ }
}
- }
init {
contentResolver.registerContentObserver(
- Settings.Global.getUriFor(Settings.Global.ANIMATOR_DURATION_SCALE),
- /* notifyForDescendants= */ false,
- animatorDurationScaleObserver)
+ Settings.Global.getUriFor(Settings.Global.ANIMATOR_DURATION_SCALE),
+ /* notifyForDescendants= */ false,
+ animatorDurationScaleObserver)
onAnimatorScaleChanged()
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProvider.kt
index a274b74f336b..7b6791770295 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProvider.kt
@@ -20,16 +20,18 @@ import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionPr
/**
* Manages progress listeners that can have smaller lifespan than the unfold animation.
+ *
* Allows to limit getting transition updates to only when
- * [ScopedUnfoldTransitionProgressProvider.setReadyToHandleTransition] is called
- * with readyToHandleTransition = true
+ * [ScopedUnfoldTransitionProgressProvider.setReadyToHandleTransition] is called with
+ * readyToHandleTransition = true
*
- * If the transition has already started by the moment when the clients are ready to play
- * the transition then it will report transition started callback and current animation progress.
+ * If the transition has already started by the moment when the clients are ready to play the
+ * transition then it will report transition started callback and current animation progress.
*/
-class ScopedUnfoldTransitionProgressProvider @JvmOverloads constructor(
- source: UnfoldTransitionProgressProvider? = null
-) : UnfoldTransitionProgressProvider, TransitionProgressListener {
+class ScopedUnfoldTransitionProgressProvider
+@JvmOverloads
+constructor(source: UnfoldTransitionProgressProvider? = null) :
+ UnfoldTransitionProgressProvider, TransitionProgressListener {
private var source: UnfoldTransitionProgressProvider? = null
@@ -43,8 +45,8 @@ class ScopedUnfoldTransitionProgressProvider @JvmOverloads constructor(
setSourceProvider(source)
}
/**
- * Sets the source for the unfold transition progress updates,
- * it replaces current provider if it is already set
+ * Sets the source for the unfold transition progress updates. Replaces current provider if it
+ * is already set
* @param provider transition provider that emits transition progress updates
*/
fun setSourceProvider(provider: UnfoldTransitionProgressProvider?) {
@@ -60,8 +62,10 @@ class ScopedUnfoldTransitionProgressProvider @JvmOverloads constructor(
/**
* Allows to notify this provide whether the listeners can play the transition or not.
- * Call this method with readyToHandleTransition = true when all listeners
- * are ready to consume the transition progress events.
+ *
+ * Call this method with readyToHandleTransition = true when all listeners are ready to consume
+ * the transition progress events.
+ *
* Call it with readyToHandleTransition = false when listeners can't process the events.
*/
fun setReadyToHandleTransition(isReadyToHandleTransition: Boolean) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
index cc6df45c598f..d02b8752469a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
@@ -30,7 +30,6 @@ import com.android.systemui.R;
*/
public abstract class KeyguardAbsKeyInputView extends KeyguardInputView {
protected View mEcaView;
- protected boolean mEnableHaptics;
// To avoid accidental lockout due to events while the device in in the pocket, ignore
// any passwords with length less than or equal to this length.
@@ -45,10 +44,6 @@ public abstract class KeyguardAbsKeyInputView extends KeyguardInputView {
super(context, attrs);
}
- void setEnableHaptics(boolean enableHaptics) {
- mEnableHaptics = enableHaptics;
- }
-
protected abstract int getPasswordTextViewId();
protected abstract void resetState();
@@ -80,11 +75,9 @@ public abstract class KeyguardAbsKeyInputView extends KeyguardInputView {
// Cause a VIRTUAL_KEY vibration
public void doHapticKeyClick() {
- if (mEnableHaptics) {
- performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
- HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING
- | HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
- }
+ performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
+ HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING
+ | HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
}
public void setKeyDownListener(KeyDownListener keyDownListener) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
index 1c4559eb0364..f86d08de87fc 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
@@ -98,7 +98,6 @@ public abstract class KeyguardAbsKeyInputViewController<T extends KeyguardAbsKey
protected void onViewAttached() {
super.onViewAttached();
mView.setKeyDownListener(mKeyDownListener);
- mView.setEnableHaptics(mLockPatternUtils.isTactileFeedbackEnabled());
mEmergencyButtonController.setEmergencyButtonCallback(mEmergencyButtonCallback);
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 25b551139b44..8eb528980a96 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -20,6 +20,7 @@ import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import static com.android.keyguard.KeyguardClockSwitch.LARGE;
+import static com.android.keyguard.KeyguardClockSwitch.SMALL;
import android.app.WallpaperManager;
import android.content.res.Resources;
@@ -41,7 +42,6 @@ import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.plugins.ClockPlugin;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController;
import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController;
import com.android.systemui.statusbar.notification.AnimatableProperty;
import com.android.systemui.statusbar.notification.PropertyAnimator;
@@ -86,6 +86,9 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
private AnimatableClockController mLargeClockViewController;
private FrameLayout mLargeClockFrame; // centered clock
+ @KeyguardClockSwitch.ClockSize
+ private int mCurrentClockSize = SMALL;
+
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final KeyguardBypassController mBypassController;
@@ -110,7 +113,6 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
private View mSmartspaceView;
private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
- private SmartspaceTransitionController mSmartspaceTransitionController;
private boolean mOnlyClock = false;
private Executor mUiExecutor;
@@ -136,7 +138,6 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
KeyguardBypassController bypassController,
LockscreenSmartspaceController smartspaceController,
KeyguardUnlockAnimationController keyguardUnlockAnimationController,
- SmartspaceTransitionController smartspaceTransitionController,
SecureSettings secureSettings,
@Main Executor uiExecutor,
@Main Resources resources) {
@@ -155,7 +156,22 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
mSecureSettings = secureSettings;
mUiExecutor = uiExecutor;
mKeyguardUnlockAnimationController = keyguardUnlockAnimationController;
- mSmartspaceTransitionController = smartspaceTransitionController;
+ mKeyguardUnlockAnimationController.addKeyguardUnlockAnimationListener(
+ new KeyguardUnlockAnimationController.KeyguardUnlockAnimationListener() {
+ @Override
+ public void onSmartspaceSharedElementTransitionStarted() {
+ // The smartspace needs to be able to translate out of bounds in order to
+ // end up where the launcher's smartspace is, while its container is being
+ // swiped off the top of the screen.
+ setClipChildrenForUnlock(false);
+ }
+
+ @Override
+ public void onUnlockAnimationFinished() {
+ // For performance reasons, reset this once the unlock animation ends.
+ setClipChildrenForUnlock(true);
+ }
+ });
}
/**
@@ -236,7 +252,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
mSmartspaceView.setPaddingRelative(startPadding, 0, endPadding, 0);
updateClockLayout();
- mSmartspaceTransitionController.setLockscreenSmartspace(mSmartspaceView);
+ mKeyguardUnlockAnimationController.setLockscreenSmartspace(mSmartspaceView);
}
mSecureSettings.registerContentObserver(
@@ -293,6 +309,8 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
return;
}
+ mCurrentClockSize = clockSize;
+
boolean appeared = mView.switchToClock(clockSize, animate);
if (animate && appeared && clockSize == LARGE) {
mLargeClockViewController.animateAppear();
@@ -368,7 +386,14 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
final Set<View> excludedViews = new HashSet<>();
if (mSmartspaceView != null) {
- excludedViews.add(mSmartspaceView);
+ excludedViews.add(mStatusArea);
+ }
+
+ // Don't change the alpha of the invisible clock.
+ if (mCurrentClockSize == LARGE) {
+ excludedViews.add(mClockFrame);
+ } else {
+ excludedViews.add(mLargeClockFrame);
}
setChildrenAlphaExcluding(alpha, excludedViews);
@@ -449,4 +474,16 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
mUiExecutor.execute(() -> displayClock(KeyguardClockSwitch.SMALL, /* animate */ true));
}
}
+
+ /**
+ * Sets the clipChildren property on relevant views, to allow the smartspace to draw out of
+ * bounds during the unlock transition.
+ */
+ private void setClipChildrenForUnlock(boolean clip) {
+ mView.setClipChildren(clip);
+
+ if (mStatusArea != null) {
+ mStatusArea.setClipChildren(clip);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardConstants.java b/packages/SystemUI/src/com/android/keyguard/KeyguardConstants.java
index 0340904cbd9d..b2658c9f9bdb 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardConstants.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardConstants.java
@@ -16,6 +16,8 @@
package com.android.keyguard;
+import android.util.Log;
+
/**
* Defines constants for the Keyguard.
*/
@@ -25,7 +27,7 @@ public class KeyguardConstants {
* Turns on debugging information for the whole Keyguard. This is very verbose and should only
* be used temporarily for debugging.
*/
- public static final boolean DEBUG = false;
+ public static final boolean DEBUG = Log.isLoggable("Keyguard", Log.DEBUG);
public static final boolean DEBUG_SIM_STATES = true;
public static final boolean DEBUG_BIOMETRIC_WAKELOCK = true;
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
index 8c7ede26e2e6..848b8ab8ff84 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
@@ -262,7 +262,6 @@ public class KeyguardDisplayManager {
private static final int VIDEO_SAFE_REGION = 80; // Percentage of display width & height
private static final int MOVE_CLOCK_TIMEOUT = 10000; // 10s
private final KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory;
- private final Context mContext;
private KeyguardClockSwitchController mKeyguardClockSwitchController;
private View mClock;
private int mUsableWidth;
@@ -286,7 +285,6 @@ public class KeyguardDisplayManager {
WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory;
setCancelable(false);
- mContext = context;
}
@Override
@@ -311,7 +309,7 @@ public class KeyguardDisplayManager {
updateBounds();
- setContentView(LayoutInflater.from(mContext)
+ setContentView(LayoutInflater.from(getContext())
.inflate(R.layout.keyguard_presentation, null));
// Logic to make the lock screen fullscreen
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
index 94e07b713915..238acd5db621 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
@@ -224,8 +224,6 @@ public class KeyguardPatternViewController
mLockPatternView.setSaveEnabled(false);
mLockPatternView.setInStealthMode(!mLockPatternUtils.isVisiblePatternEnabled(
KeyguardUpdateMonitor.getCurrentUser()));
- // vibrate mode will be the same for the life of this screen
- mLockPatternView.setTactileFeedbackEnabled(mLockPatternUtils.isTactileFeedbackEnabled());
mLockPatternView.setOnTouchListener((v, event) -> {
if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
mFalsingCollector.avoidGesture();
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index 8bf890d7df50..a5fe0efc8887 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -22,7 +22,6 @@ import android.util.Slog;
import com.android.keyguard.KeyguardClockSwitch.ClockSize;
import com.android.systemui.communal.CommunalStateController;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
-import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController;
import com.android.systemui.statusbar.notification.AnimatableProperty;
import com.android.systemui.statusbar.notification.PropertyAnimator;
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
@@ -55,7 +54,6 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
private final KeyguardVisibilityHelper mKeyguardVisibilityHelper;
private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
private final KeyguardStateController mKeyguardStateController;
- private SmartspaceTransitionController mSmartspaceTransitionController;
private final Rect mClipBounds = new Rect();
@Inject
@@ -69,7 +67,6 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
ConfigurationController configurationController,
DozeParameters dozeParameters,
KeyguardUnlockAnimationController keyguardUnlockAnimationController,
- SmartspaceTransitionController smartspaceTransitionController,
ScreenOffAnimationController screenOffAnimationController) {
super(keyguardStatusView);
mKeyguardSliceViewController = keyguardSliceViewController;
@@ -82,7 +79,6 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
keyguardStateController, dozeParameters, screenOffAnimationController,
/* animateYPos= */ true, /* visibleOnCommunal= */ false);
mKeyguardUnlockAnimationController = keyguardUnlockAnimationController;
- mSmartspaceTransitionController = smartspaceTransitionController;
}
@Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index a348b423d3c7..f2d0427c39d3 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -37,8 +37,6 @@ import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.ActivityTaskManager.RootTaskInfo;
import android.app.AlarmManager;
-import android.app.Notification;
-import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.UserSwitchObserver;
import android.app.admin.DevicePolicyManager;
@@ -104,8 +102,6 @@ import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.TaskStackChangeListeners;
@@ -113,7 +109,6 @@ import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.telephony.TelephonyListenerManager;
import com.android.systemui.util.Assert;
-import com.android.systemui.util.NotificationChannels;
import com.android.systemui.util.RingerModeTracker;
import com.google.android.collect.Lists;
@@ -338,7 +333,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
private final Executor mBackgroundExecutor;
private SensorPrivacyManager mSensorPrivacyManager;
- private FeatureFlags mFeatureFlags;
private int mFaceAuthUserId;
/**
@@ -443,7 +437,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
@Override
- public void onTrustChanged(boolean enabled, int userId, int flags) {
+ public void onTrustChanged(boolean enabled, int userId, int flags,
+ List<String> trustGrantedMessages) {
Assert.isMainThread();
boolean wasTrusted = mUserHasTrust.get(userId, false);
mUserHasTrust.put(userId, enabled);
@@ -465,6 +460,19 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
}
}
+
+ if (KeyguardUpdateMonitor.getCurrentUser() == userId && getUserHasTrust(userId)) {
+ CharSequence message = null;
+ if (trustGrantedMessages != null && trustGrantedMessages.size() > 0) {
+ message = trustGrantedMessages.get(0); // for now only shows the first in the list
+ }
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.showTrustGrantedMessage(message);
+ }
+ }
+ }
}
@Override
@@ -1790,8 +1798,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
AuthController authController,
TelephonyListenerManager telephonyListenerManager,
InteractionJankMonitor interactionJankMonitor,
- LatencyTracker latencyTracker,
- FeatureFlags featureFlags) {
+ LatencyTracker latencyTracker) {
mContext = context;
mSubscriptionManager = SubscriptionManager.from(context);
mTelephonyListenerManager = telephonyListenerManager;
@@ -1809,7 +1816,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
mAuthController = authController;
dumpManager.registerDumpable(getClass().getName(), this);
mSensorPrivacyManager = context.getSystemService(SensorPrivacyManager.class);
- mFeatureFlags = featureFlags;
mHandler = new Handler(mainLooper) {
@Override
@@ -2253,34 +2259,12 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
return;
}
- if (shouldTriggerActiveUnlock() && mFeatureFlags.isEnabled(Flags.ACTIVE_UNLOCK)) {
- // TODO (b/192405661): call new TrustManager API
- mNumActiveUnlockTriggers++;
- Log.d("ActiveUnlock", "would have triggered times=" + mNumActiveUnlockTriggers);
- showActiveUnlockNotification(mNumActiveUnlockTriggers);
+ if (shouldTriggerActiveUnlock()) {
+ mTrustManager.reportUserRequestedUnlock(KeyguardUpdateMonitor.getCurrentUser());
}
}
- /**
- * TODO (b/192405661): Only for testing. Remove before release.
- */
- private void showActiveUnlockNotification(int times) {
- final String message = "Active unlock triggered " + times + " times.";
- final Notification.Builder nb =
- new Notification.Builder(mContext, NotificationChannels.GENERAL)
- .setSmallIcon(R.drawable.ic_volume_ringer)
- .setContentTitle(message)
- .setStyle(new Notification.BigTextStyle().bigText(message));
- mContext.getSystemService(NotificationManager.class).notifyAsUser(
- "active_unlock",
- 0,
- nb.build(),
- UserHandle.ALL);
- }
-
private boolean shouldTriggerActiveUnlock() {
- // TODO: check if active unlock is ENABLED / AVAILABLE
-
// Triggers:
final boolean triggerActiveUnlockForAssistant = shouldTriggerActiveUnlockForAssistant();
final boolean awakeKeyguard = mKeyguardIsVisible && mDeviceInteractive && !mGoingToSleep
@@ -2294,7 +2278,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
final boolean userCanDismissLockScreen = getUserCanSkipBouncer(user)
|| !mLockPatternUtils.isSecure(user);
- // Don't trigger active unlock if fp is locked out TODO: confirm this one
+ // Don't trigger active unlock if fp is locked out
final boolean fpLockedout = mFingerprintLockedOut || mFingerprintLockedOutPermanent;
// Don't trigger active unlock if primary auth is required
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
index a74fd15ab11b..47e1035fbfef 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -23,6 +23,8 @@ import android.os.SystemClock;
import android.telephony.TelephonyManager;
import android.view.WindowManagerPolicyConstants;
+import androidx.annotation.Nullable;
+
import com.android.settingslib.fuelgauge.BatteryStatus;
import com.android.systemui.statusbar.KeyguardIndicationController;
@@ -216,6 +218,11 @@ public class KeyguardUpdateMonitorCallback {
public void onTrustGrantedWithFlags(int flags, int userId) { }
/**
+ * Called when setting the trust granted message.
+ */
+ public void showTrustGrantedMessage(@Nullable CharSequence message) { }
+
+ /**
* Called when a biometric has been acquired.
* <p>
* It is guaranteed that either {@link #onBiometricAuthenticated} or
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
index e79ea9a44843..a5a3f80d07b9 100644
--- a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
@@ -213,10 +213,8 @@ public class NumPadKey extends ViewGroup {
// Cause a VIRTUAL_KEY vibration
public void doHapticKeyClick() {
- if (mLockPatternUtils.isTactileFeedbackEnabled()) {
- performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
- HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING
- | HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
- }
+ performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
+ HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING
+ | HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 276790483861..b3be87731fbc 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -122,7 +122,8 @@ public class SystemUIFactory {
.setTaskSurfaceHelper(mWMComponent.getTaskSurfaceHelper())
.setRecentTasks(mWMComponent.getRecentTasks())
.setCompatUI(Optional.of(mWMComponent.getCompatUI()))
- .setDragAndDrop(Optional.of(mWMComponent.getDragAndDrop()));
+ .setDragAndDrop(Optional.of(mWMComponent.getDragAndDrop()))
+ .setBackAnimation(mWMComponent.getBackAnimation());
} else {
// TODO: Call on prepareSysUIComponentBuilder but not with real components. Other option
// is separating this logic into newly creating SystemUITestsFactory.
@@ -142,7 +143,8 @@ public class SystemUIFactory {
.setTaskSurfaceHelper(Optional.ofNullable(null))
.setRecentTasks(Optional.ofNullable(null))
.setCompatUI(Optional.ofNullable(null))
- .setDragAndDrop(Optional.ofNullable(null));
+ .setDragAndDrop(Optional.ofNullable(null))
+ .setBackAnimation(Optional.ofNullable(null));
}
mSysUIComponent = builder.build();
if (mInitializeComponents) {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
index 052ec86d8398..dbd215d9c713 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
@@ -22,8 +22,10 @@ import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_M
import android.annotation.NonNull;
import android.annotation.UiContext;
+import android.content.ComponentCallbacks;
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.Rect;
@@ -54,7 +56,8 @@ import java.util.Collections;
* The button icon is movable by dragging and it would not overlap navigation bar window.
* And the button UI would automatically be dismissed after displaying for a period of time.
*/
-class MagnificationModeSwitch implements MagnificationGestureDetector.OnGestureListener {
+class MagnificationModeSwitch implements MagnificationGestureDetector.OnGestureListener,
+ ComponentCallbacks {
@VisibleForTesting
static final long FADING_ANIMATION_DURATION_MS = 300;
@@ -75,6 +78,7 @@ class MagnificationModeSwitch implements MagnificationGestureDetector.OnGestureL
private int mMagnificationMode = ACCESSIBILITY_MAGNIFICATION_MODE_NONE;
private final LayoutParams mParams;
private final SwitchListener mSwitchListener;
+ private final Configuration mConfiguration;
@VisibleForTesting
final Rect mDraggableWindowBounds = new Rect();
private boolean mIsVisible = false;
@@ -101,6 +105,7 @@ class MagnificationModeSwitch implements MagnificationGestureDetector.OnGestureL
MagnificationModeSwitch(Context context, @NonNull ImageView imageView,
SfVsyncFrameCallbackProvider sfVsyncFrameProvider, SwitchListener switchListener) {
mContext = context;
+ mConfiguration = new Configuration(context.getResources().getConfiguration());
mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class);
mWindowManager = mContext.getSystemService(WindowManager.class);
mSfVsyncFrameProvider = sfVsyncFrameProvider;
@@ -270,6 +275,7 @@ class MagnificationModeSwitch implements MagnificationGestureDetector.OnGestureL
mIsFadeOutAnimating = false;
mImageView.setAlpha(0f);
mWindowManager.removeView(mImageView);
+ mContext.unregisterComponentCallbacks(this);
mIsVisible = false;
}
@@ -291,6 +297,8 @@ class MagnificationModeSwitch implements MagnificationGestureDetector.OnGestureL
mImageView.setImageResource(getIconResId(mode));
}
if (!mIsVisible) {
+ onConfigurationChanged(mContext.getResources().getConfiguration());
+ mContext.registerComponentCallbacks(this);
if (resetPosition) {
mDraggableWindowBounds.set(getDraggableWindowBounds());
mParams.x = mDraggableWindowBounds.right;
@@ -321,7 +329,21 @@ class MagnificationModeSwitch implements MagnificationGestureDetector.OnGestureL
}
}
+ @Override
+ public void onConfigurationChanged(@NonNull Configuration newConfig) {
+ final int configDiff = newConfig.diff(mConfiguration);
+ mConfiguration.setTo(newConfig);
+ onConfigurationChanged(configDiff);
+ }
+
+ @Override
+ public void onLowMemory() {
+ }
+
void onConfigurationChanged(int configDiff) {
+ if (configDiff == 0) {
+ return;
+ }
if ((configDiff & (ActivityInfo.CONFIG_ORIENTATION | ActivityInfo.CONFIG_SCREEN_SIZE))
!= 0) {
final Rect previousDraggableBounds = new Rect(mDraggableWindowBounds);
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
index 4784bc12099b..885a1777f30b 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
@@ -23,7 +23,6 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_M
import android.annotation.MainThread;
import android.annotation.Nullable;
import android.content.Context;
-import android.content.res.Configuration;
import android.graphics.Rect;
import android.hardware.display.DisplayManager;
import android.os.Handler;
@@ -65,7 +64,6 @@ public class WindowMagnification extends CoreStartable implements WindowMagnifie
private final OverviewProxyService mOverviewProxyService;
private WindowMagnificationConnectionImpl mWindowMagnificationConnectionImpl;
- private Configuration mLastConfiguration;
private SysUiState mSysUiState;
private static class ControllerSupplier extends
@@ -107,7 +105,6 @@ public class WindowMagnification extends CoreStartable implements WindowMagnifie
SysUiState sysUiState, OverviewProxyService overviewProxyService) {
super(context);
mHandler = mainHandler;
- mLastConfiguration = new Configuration(context.getResources().getConfiguration());
mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class);
mCommandQueue = commandQueue;
mModeSwitchesController = modeSwitchesController;
@@ -118,18 +115,6 @@ public class WindowMagnification extends CoreStartable implements WindowMagnifie
}
@Override
- public void onConfigurationChanged(Configuration newConfig) {
- final int configDiff = newConfig.diff(mLastConfiguration);
- mLastConfiguration.setTo(newConfig);
- mMagnificationControllerSupplier.forEach(
- magnificationController -> magnificationController.onConfigurationChanged(
- configDiff));
- if (mModeSwitchesController != null) {
- mModeSwitchesController.onConfigurationChanged(configDiff);
- }
- }
-
- @Override
public void start() {
mCommandQueue.addCallback(this);
mOverviewProxyService.addCallback(new OverviewProxyService.OverviewProxyListener() {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index aa1a43397f65..0d20403b08f2 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -26,6 +26,7 @@ import android.animation.PropertyValuesHolder;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UiContext;
+import android.content.ComponentCallbacks;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
@@ -76,7 +77,8 @@ import java.util.Locale;
* Class to handle adding and removing a window magnification.
*/
class WindowMagnificationController implements View.OnTouchListener, SurfaceHolder.Callback,
- MirrorWindowControl.MirrorWindowDelegate, MagnificationGestureDetector.OnGestureListener {
+ MirrorWindowControl.MirrorWindowDelegate, MagnificationGestureDetector.OnGestureListener,
+ ComponentCallbacks {
private static final String TAG = "WindowMagnificationController";
@SuppressWarnings("isloggabletaglength")
@@ -143,6 +145,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
private View mTopDrag;
private View mRightDrag;
private View mBottomDrag;
+ private final Configuration mConfiguration;
@NonNull
private final WindowMagnifierCallback mWindowMagnifierCallback;
@@ -191,6 +194,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
mSfVsyncFrameProvider = sfVsyncFrameProvider;
mWindowMagnifierCallback = callback;
mSysUiState = sysUiState;
+ mConfiguration = new Configuration(context.getResources().getConfiguration());
final Display display = mContext.getDisplay();
mDisplayId = mContext.getDisplayId();
@@ -339,6 +343,18 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
}
mMirrorViewBounds.setEmpty();
updateSystemUIStateIfNeeded();
+ mContext.unregisterComponentCallbacks(this);
+ }
+
+ @Override
+ public void onConfigurationChanged(@NonNull Configuration newConfig) {
+ final int configDiff = newConfig.diff(mConfiguration);
+ mConfiguration.setTo(newConfig);
+ onConfigurationChanged(configDiff);
+ }
+
+ @Override
+ public void onLowMemory() {
}
/**
@@ -351,6 +367,9 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
Log.d(TAG, "onConfigurationChanged = " + Configuration.configurationDiffToString(
configDiff));
}
+ if (configDiff == 0) {
+ return;
+ }
if ((configDiff & ActivityInfo.CONFIG_ORIENTATION) != 0) {
onRotate();
}
@@ -390,7 +409,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
if (currentWindowBounds.equals(oldWindowBounds)) {
if (DEBUG) {
- Log.d(TAG, "updateMagnificationFrame -- window bounds is not changed");
+ Log.d(TAG, "handleScreenSizeChanged -- window bounds is not changed");
}
return false;
}
@@ -851,6 +870,10 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
deleteWindowMagnification();
return;
}
+ if (!isWindowVisible()) {
+ onConfigurationChanged(mResources.getConfiguration());
+ mContext.registerComponentCallbacks(this);
+ }
mMagnificationFrameOffsetX = Float.isNaN(magnificationFrameOffsetRatioX)
? mMagnificationFrameOffsetX
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java
index ab8162f9464d..11498dbc0b83 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java
@@ -107,6 +107,5 @@ public class AuthCredentialPatternView extends AuthCredentialView {
mLockPatternView.setOnPatternListener(new UnlockPatternListener());
mLockPatternView.setInStealthMode(
!mLockPatternUtils.isVisiblePatternEnabled(mUserId));
- mLockPatternView.setTactileFeedbackEnabled(mLockPatternUtils.isTactileFeedbackEnabled());
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java
index e7015115d84c..1317492aefac 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java
@@ -29,9 +29,7 @@ import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Looper;
import android.view.animation.AccelerateDecelerateInterpolator;
-import android.view.animation.LinearInterpolator;
-import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -43,18 +41,11 @@ import com.android.systemui.R;
public class UdfpsEnrollDrawable extends UdfpsDrawable {
private static final String TAG = "UdfpsAnimationEnroll";
- private static final long HINT_COLOR_ANIM_DELAY_MS = 233L;
- private static final long HINT_COLOR_ANIM_DURATION_MS = 517L;
- private static final long HINT_WIDTH_ANIM_DURATION_MS = 233L;
private static final long TARGET_ANIM_DURATION_LONG = 800L;
private static final long TARGET_ANIM_DURATION_SHORT = 600L;
// 1 + SCALE_MAX is the maximum that the moving target will animate to
private static final float SCALE_MAX = 0.25f;
- private static final float HINT_PADDING_DP = 10f;
- private static final float HINT_MAX_WIDTH_DP = 6f;
- private static final float HINT_ANGLE = 40f;
-
private final Handler mHandler = new Handler(Looper.getMainLooper());
@NonNull private final Drawable mMovingTargetFpIcon;
@@ -72,30 +63,10 @@ public class UdfpsEnrollDrawable extends UdfpsDrawable {
// Moving target size
float mCurrentScale = 1.f;
- @ColorInt private final int mHintColorFaded;
- @ColorInt private final int mHintColorHighlight;
- private final float mHintMaxWidthPx;
- private final float mHintPaddingPx;
-
@NonNull private final Animator.AnimatorListener mTargetAnimListener;
private boolean mShouldShowTipHint = false;
- @NonNull private final Paint mTipHintPaint;
- @Nullable private AnimatorSet mTipHintAnimatorSet;
- @Nullable private ValueAnimator mTipHintColorAnimator;
- @Nullable private ValueAnimator mTipHintWidthAnimator;
- @NonNull private final ValueAnimator.AnimatorUpdateListener mTipHintColorUpdateListener;
- @NonNull private final ValueAnimator.AnimatorUpdateListener mTipHintWidthUpdateListener;
- @NonNull private final Animator.AnimatorListener mTipHintPulseListener;
-
private boolean mShouldShowEdgeHint = false;
- @NonNull private final Paint mEdgeHintPaint;
- @Nullable private AnimatorSet mEdgeHintAnimatorSet;
- @Nullable private ValueAnimator mEdgeHintColorAnimator;
- @Nullable private ValueAnimator mEdgeHintWidthAnimator;
- @NonNull private final ValueAnimator.AnimatorUpdateListener mEdgeHintColorUpdateListener;
- @NonNull private final ValueAnimator.AnimatorUpdateListener mEdgeHintWidthUpdateListener;
- @NonNull private final Animator.AnimatorListener mEdgeHintPulseListener;
UdfpsEnrollDrawable(@NonNull Context context) {
super(context);
@@ -117,11 +88,6 @@ public class UdfpsEnrollDrawable extends UdfpsDrawable {
getFingerprintDrawable().setTint(context.getColor(R.color.udfps_enroll_icon));
- mHintColorFaded = context.getColor(R.color.udfps_moving_target_fill);
- mHintColorHighlight = context.getColor(R.color.udfps_enroll_progress);
- mHintMaxWidthPx = Utils.dpToPixels(context, HINT_MAX_WIDTH_DP);
- mHintPaddingPx = Utils.dpToPixels(context, HINT_PADDING_DP);
-
mTargetAnimListener = new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {}
@@ -137,80 +103,6 @@ public class UdfpsEnrollDrawable extends UdfpsDrawable {
@Override
public void onAnimationRepeat(Animator animation) {}
};
-
- mTipHintPaint = new Paint(0 /* flags */);
- mTipHintPaint.setAntiAlias(true);
- mTipHintPaint.setColor(mHintColorFaded);
- mTipHintPaint.setStyle(Paint.Style.STROKE);
- mTipHintPaint.setStrokeCap(Paint.Cap.ROUND);
- mTipHintPaint.setStrokeWidth(0f);
- mTipHintColorUpdateListener = animation -> {
- mTipHintPaint.setColor((int) animation.getAnimatedValue());
- invalidateSelf();
- };
- mTipHintWidthUpdateListener = animation -> {
- mTipHintPaint.setStrokeWidth((float) animation.getAnimatedValue());
- invalidateSelf();
- };
- mTipHintPulseListener = new Animator.AnimatorListener() {
- @Override
- public void onAnimationStart(Animator animation) {}
-
- @Override
- public void onAnimationEnd(Animator animation) {
- mHandler.postDelayed(() -> {
- mTipHintColorAnimator =
- ValueAnimator.ofArgb(mTipHintPaint.getColor(), mHintColorFaded);
- mTipHintColorAnimator.setInterpolator(new LinearInterpolator());
- mTipHintColorAnimator.setDuration(HINT_COLOR_ANIM_DURATION_MS);
- mTipHintColorAnimator.addUpdateListener(mTipHintColorUpdateListener);
- mTipHintColorAnimator.start();
- }, HINT_COLOR_ANIM_DELAY_MS);
- }
-
- @Override
- public void onAnimationCancel(Animator animation) {}
-
- @Override
- public void onAnimationRepeat(Animator animation) {}
- };
-
- mEdgeHintPaint = new Paint(0 /* flags */);
- mEdgeHintPaint.setAntiAlias(true);
- mEdgeHintPaint.setColor(mHintColorFaded);
- mEdgeHintPaint.setStyle(Paint.Style.STROKE);
- mEdgeHintPaint.setStrokeCap(Paint.Cap.ROUND);
- mEdgeHintPaint.setStrokeWidth(0f);
- mEdgeHintColorUpdateListener = animation -> {
- mEdgeHintPaint.setColor((int) animation.getAnimatedValue());
- invalidateSelf();
- };
- mEdgeHintWidthUpdateListener = animation -> {
- mEdgeHintPaint.setStrokeWidth((float) animation.getAnimatedValue());
- invalidateSelf();
- };
- mEdgeHintPulseListener = new Animator.AnimatorListener() {
- @Override
- public void onAnimationStart(Animator animation) {}
-
- @Override
- public void onAnimationEnd(Animator animation) {
- mHandler.postDelayed(() -> {
- mEdgeHintColorAnimator =
- ValueAnimator.ofArgb(mEdgeHintPaint.getColor(), mHintColorFaded);
- mEdgeHintColorAnimator.setInterpolator(new LinearInterpolator());
- mEdgeHintColorAnimator.setDuration(HINT_COLOR_ANIM_DURATION_MS);
- mEdgeHintColorAnimator.addUpdateListener(mEdgeHintColorUpdateListener);
- mEdgeHintColorAnimator.start();
- }, HINT_COLOR_ANIM_DELAY_MS);
- }
-
- @Override
- public void onAnimationCancel(Animator animation) {}
-
- @Override
- public void onAnimationRepeat(Animator animation) {}
- };
}
void setEnrollHelper(@NonNull UdfpsEnrollHelper helper) {
@@ -287,25 +179,12 @@ public class UdfpsEnrollDrawable extends UdfpsDrawable {
private void updateTipHintVisibility() {
final boolean shouldShow = mEnrollHelper != null && mEnrollHelper.isTipEnrollmentStage();
+ // With the new update, we will git rid of most of this code, and instead
+ // we will change the fingerprint icon.
if (mShouldShowTipHint == shouldShow) {
return;
}
mShouldShowTipHint = shouldShow;
-
- if (mTipHintWidthAnimator != null && mTipHintWidthAnimator.isRunning()) {
- mTipHintWidthAnimator.cancel();
- }
-
- final float targetWidth = shouldShow ? mHintMaxWidthPx : 0f;
- mTipHintWidthAnimator = ValueAnimator.ofFloat(mTipHintPaint.getStrokeWidth(), targetWidth);
- mTipHintWidthAnimator.setDuration(HINT_WIDTH_ANIM_DURATION_MS);
- mTipHintWidthAnimator.addUpdateListener(mTipHintWidthUpdateListener);
-
- if (shouldShow) {
- startTipHintPulseAnimation();
- } else {
- mTipHintWidthAnimator.start();
- }
}
private void updateEdgeHintVisibility() {
@@ -314,71 +193,6 @@ public class UdfpsEnrollDrawable extends UdfpsDrawable {
return;
}
mShouldShowEdgeHint = shouldShow;
-
- if (mEdgeHintWidthAnimator != null && mEdgeHintWidthAnimator.isRunning()) {
- mEdgeHintWidthAnimator.cancel();
- }
-
- final float targetWidth = shouldShow ? mHintMaxWidthPx : 0f;
- mEdgeHintWidthAnimator =
- ValueAnimator.ofFloat(mEdgeHintPaint.getStrokeWidth(), targetWidth);
- mEdgeHintWidthAnimator.setDuration(HINT_WIDTH_ANIM_DURATION_MS);
- mEdgeHintWidthAnimator.addUpdateListener(mEdgeHintWidthUpdateListener);
-
- if (shouldShow) {
- startEdgeHintPulseAnimation();
- } else {
- mEdgeHintWidthAnimator.start();
- }
- }
-
- private void startTipHintPulseAnimation() {
- mHandler.removeCallbacksAndMessages(null);
- if (mTipHintAnimatorSet != null && mTipHintAnimatorSet.isRunning()) {
- mTipHintAnimatorSet.cancel();
- }
- if (mTipHintColorAnimator != null && mTipHintColorAnimator.isRunning()) {
- mTipHintColorAnimator.cancel();
- }
-
- mTipHintColorAnimator = ValueAnimator.ofArgb(mTipHintPaint.getColor(), mHintColorHighlight);
- mTipHintColorAnimator.setDuration(HINT_WIDTH_ANIM_DURATION_MS);
- mTipHintColorAnimator.addUpdateListener(mTipHintColorUpdateListener);
- mTipHintColorAnimator.addListener(mTipHintPulseListener);
-
- mTipHintAnimatorSet = new AnimatorSet();
- mTipHintAnimatorSet.setInterpolator(new AccelerateDecelerateInterpolator());
- mTipHintAnimatorSet.playTogether(mTipHintColorAnimator, mTipHintWidthAnimator);
- mTipHintAnimatorSet.start();
- }
-
- private void startEdgeHintPulseAnimation() {
- mHandler.removeCallbacksAndMessages(null);
- if (mEdgeHintAnimatorSet != null && mEdgeHintAnimatorSet.isRunning()) {
- mEdgeHintAnimatorSet.cancel();
- }
- if (mEdgeHintColorAnimator != null && mEdgeHintColorAnimator.isRunning()) {
- mEdgeHintColorAnimator.cancel();
- }
-
- mEdgeHintColorAnimator =
- ValueAnimator.ofArgb(mEdgeHintPaint.getColor(), mHintColorHighlight);
- mEdgeHintColorAnimator.setDuration(HINT_WIDTH_ANIM_DURATION_MS);
- mEdgeHintColorAnimator.addUpdateListener(mEdgeHintColorUpdateListener);
- mEdgeHintColorAnimator.addListener(mEdgeHintPulseListener);
-
- mEdgeHintAnimatorSet = new AnimatorSet();
- mEdgeHintAnimatorSet.setInterpolator(new AccelerateDecelerateInterpolator());
- mEdgeHintAnimatorSet.playTogether(mEdgeHintColorAnimator, mEdgeHintWidthAnimator);
- mEdgeHintAnimatorSet.start();
- }
-
- private boolean isTipHintVisible() {
- return mTipHintPaint.getStrokeWidth() > 0f;
- }
-
- private boolean isEdgeHintVisible() {
- return mEdgeHintPaint.getStrokeWidth() > 0f;
}
@Override
@@ -409,58 +223,6 @@ public class UdfpsEnrollDrawable extends UdfpsDrawable {
mSensorOutlinePaint.setAlpha(getAlpha());
}
- // Draw the finger tip or edges hint.
- if (isTipHintVisible() || isEdgeHintVisible()) {
- canvas.save();
-
- // Make arcs start from the top, rather than the right.
- canvas.rotate(-90f, mSensorRect.centerX(), mSensorRect.centerY());
-
- final float halfSensorHeight = Math.abs(mSensorRect.bottom - mSensorRect.top) / 2f;
- final float halfSensorWidth = Math.abs(mSensorRect.right - mSensorRect.left) / 2f;
- final float hintXOffset = halfSensorWidth + mHintPaddingPx;
- final float hintYOffset = halfSensorHeight + mHintPaddingPx;
-
- if (isTipHintVisible()) {
- canvas.drawArc(
- mSensorRect.centerX() - hintXOffset,
- mSensorRect.centerY() - hintYOffset,
- mSensorRect.centerX() + hintXOffset,
- mSensorRect.centerY() + hintYOffset,
- -HINT_ANGLE / 2f,
- HINT_ANGLE,
- false /* useCenter */,
- mTipHintPaint);
- }
-
- if (isEdgeHintVisible()) {
- // Draw right edge hint.
- canvas.rotate(-90f, mSensorRect.centerX(), mSensorRect.centerY());
- canvas.drawArc(
- mSensorRect.centerX() - hintXOffset,
- mSensorRect.centerY() - hintYOffset,
- mSensorRect.centerX() + hintXOffset,
- mSensorRect.centerY() + hintYOffset,
- -HINT_ANGLE / 2f,
- HINT_ANGLE,
- false /* useCenter */,
- mEdgeHintPaint);
-
- // Draw left edge hint.
- canvas.rotate(180f, mSensorRect.centerX(), mSensorRect.centerY());
- canvas.drawArc(
- mSensorRect.centerX() - hintXOffset,
- mSensorRect.centerY() - hintYOffset,
- mSensorRect.centerX() + hintXOffset,
- mSensorRect.centerY() + hintYOffset,
- -HINT_ANGLE / 2f,
- HINT_ANGLE,
- false /* useCenter */,
- mEdgeHintPaint);
- }
-
- canvas.restore();
- }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
index 8b7aa093600c..3ece3ccf38fa 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
@@ -224,7 +224,8 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud
if (mUdfpsRequested && !getNotificationShadeVisible()
&& (!mIsBouncerVisible
- || mInputBouncerHiddenAmount != KeyguardBouncer.EXPANSION_VISIBLE)) {
+ || mInputBouncerHiddenAmount != KeyguardBouncer.EXPANSION_VISIBLE)
+ && mKeyguardStateController.isShowing()) {
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
index 25985181364d..8367e1128033 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
@@ -244,7 +244,8 @@ class ControlsControllerImpl @Inject constructor (
restoreFinishedReceiver,
IntentFilter(BackupHelper.ACTION_RESTORE_FINISHED),
PERMISSION_SELF,
- null
+ null,
+ Context.RECEIVER_NOT_EXPORTED
)
listingController.addCallback(listingCallback)
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
index b23569241f59..bda8e3c2ed63 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
@@ -36,6 +36,7 @@ import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider;
import com.android.wm.shell.ShellCommandHandler;
import com.android.wm.shell.TaskViewFactory;
import com.android.wm.shell.apppairs.AppPairs;
+import com.android.wm.shell.back.BackAnimation;
import com.android.wm.shell.bubbles.Bubbles;
import com.android.wm.shell.compatui.CompatUI;
import com.android.wm.shell.displayareahelper.DisplayAreaHelper;
@@ -121,6 +122,9 @@ public interface SysUIComponent {
@BindsInstance
Builder setDragAndDrop(Optional<DragAndDrop> d);
+ @BindsInstance
+ Builder setBackAnimation(Optional<BackAnimation> b);
+
SysUIComponent build();
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
index 154f6fad5998..bbe9dbd57f53 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
@@ -26,7 +26,6 @@ import com.android.systemui.accessibility.WindowMagnification;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.clipboardoverlay.ClipboardListener;
import com.android.systemui.dreams.DreamOverlayRegistrant;
-import com.android.systemui.dreams.appwidgets.ComplicationPrimer;
import com.android.systemui.globalactions.GlobalActionsComponent;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.dagger.KeyguardModule;
@@ -212,11 +211,4 @@ public abstract class SystemUIBinder {
@ClassKey(DreamOverlayRegistrant.class)
public abstract CoreStartable bindDreamOverlayRegistrant(
DreamOverlayRegistrant dreamOverlayRegistrant);
-
- /** Inject into AppWidgetOverlayPrimer. */
- @Binds
- @IntoMap
- @ClassKey(ComplicationPrimer.class)
- public abstract CoreStartable bindAppWidgetOverlayPrimer(
- ComplicationPrimer complicationPrimer);
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 9bc3f176e91a..b96c5aee4673 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -48,7 +48,6 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.recents.Recents;
import com.android.systemui.screenshot.dagger.ScreenshotModule;
import com.android.systemui.settings.dagger.SettingsModule;
-import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationShadeWindowController;
@@ -188,12 +187,6 @@ public abstract class SystemUIModule {
return SystemUIFactory.getInstance();
}
- @SysUISingleton
- @Provides
- static SmartspaceTransitionController provideSmartspaceTransitionController() {
- return new SmartspaceTransitionController();
- }
-
// TODO: This should provided by the WM component
/** Provides Optional of BubbleManager */
@SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
index b815d4e9884b..b9266923a157 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
@@ -24,6 +24,7 @@ import com.android.wm.shell.ShellCommandHandler;
import com.android.wm.shell.ShellInit;
import com.android.wm.shell.TaskViewFactory;
import com.android.wm.shell.apppairs.AppPairs;
+import com.android.wm.shell.back.BackAnimation;
import com.android.wm.shell.bubbles.Bubbles;
import com.android.wm.shell.compatui.CompatUI;
import com.android.wm.shell.dagger.TvWMShellModule;
@@ -123,4 +124,7 @@ public interface WMComponent {
@WMSingleton
DragAndDrop getDragAndDrop();
+
+ @WMSingleton
+ Optional<BackAnimation> getBackAnimation();
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
index 36319380ad50..63d4d6becc27 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
@@ -163,16 +163,12 @@ public class DozeScreenState implements DozeMachine.Part {
// Delay screen state transitions even longer while animations are running.
boolean shouldDelayTransitionEnteringDoze = newState == DOZE_AOD
- && mParameters.shouldControlScreenOff() && !turningOn;
+ && mParameters.shouldDelayDisplayDozeTransition() && !turningOn;
// Delay screen state transition longer if UDFPS is actively authenticating a fp
boolean shouldDelayTransitionForUDFPS = newState == DOZE_AOD
&& mUdfpsController != null && mUdfpsController.isFingerDown();
- if (shouldDelayTransitionEnteringDoze || shouldDelayTransitionForUDFPS) {
- mWakeLock.setAcquired(true);
- }
-
if (!messagePending) {
if (DEBUG) {
Log.d(TAG, "Display state changed to " + screenState + " delayed by "
@@ -180,6 +176,18 @@ public class DozeScreenState implements DozeMachine.Part {
}
if (shouldDelayTransitionEnteringDoze) {
+ if (justInitialized) {
+ // If we are delaying transitioning to doze and the display was not
+ // turned on we set it to 'on' first to make sure that the animation
+ // is visible before eventually moving it to doze state.
+ // The display might be off at this point for example on foldable devices
+ // when we switch displays and go to doze at the same time.
+ applyScreenState(Display.STATE_ON);
+
+ // Restore pending screen state as it gets cleared by 'applyScreenState'
+ mPendingScreenState = screenState;
+ }
+
mHandler.postDelayed(mApplyPendingScreenState, ENTER_DOZE_DELAY);
} else if (shouldDelayTransitionForUDFPS) {
mDozeLog.traceDisplayStateDelayedByUdfps(mPendingScreenState);
@@ -190,6 +198,10 @@ public class DozeScreenState implements DozeMachine.Part {
} else if (DEBUG) {
Log.d(TAG, "Pending display state change to " + screenState);
}
+
+ if (shouldDelayTransitionEnteringDoze || shouldDelayTransitionForUDFPS) {
+ mWakeLock.setAcquired(true);
+ }
} else if (turningOff) {
mDozeHost.prepareForGentleSleep(() -> applyScreenState(screenState));
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/ComplicationProvider.java b/packages/SystemUI/src/com/android/systemui/dreams/ComplicationProvider.java
index 099e37960ad6..e5d63192f156 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/ComplicationProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/ComplicationProvider.java
@@ -16,8 +16,14 @@
package com.android.systemui.dreams;
+import android.annotation.IntDef;
import android.content.Context;
+import com.android.settingslib.dream.DreamBackend;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* {@link ComplicationProvider} is an interface for defining entities that can supply complications
* to show over a dream. Presentation components such as the {@link DreamOverlayService} supply
@@ -25,6 +31,27 @@ import android.content.Context;
*/
public interface ComplicationProvider {
/**
+ * The type of dream complications which can be provided by a {@link ComplicationProvider}.
+ */
+ @IntDef(prefix = {"COMPLICATION_TYPE_"}, flag = true, value = {
+ COMPLICATION_TYPE_NONE,
+ COMPLICATION_TYPE_TIME,
+ COMPLICATION_TYPE_DATE,
+ COMPLICATION_TYPE_WEATHER,
+ COMPLICATION_TYPE_AIR_QUALITY,
+ COMPLICATION_TYPE_CAST_INFO
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface ComplicationType {}
+
+ int COMPLICATION_TYPE_NONE = 0;
+ int COMPLICATION_TYPE_TIME = 1;
+ int COMPLICATION_TYPE_DATE = 1 << 1;
+ int COMPLICATION_TYPE_WEATHER = 1 << 2;
+ int COMPLICATION_TYPE_AIR_QUALITY = 1 << 3;
+ int COMPLICATION_TYPE_CAST_INFO = 1 << 4;
+
+ /**
* Called when the {@link ComplicationHost} requests the associated complication be produced.
*
* @param context The {@link Context} used to construct the view.
@@ -33,4 +60,26 @@ public interface ComplicationProvider {
*/
void onCreateComplication(Context context, ComplicationHost.CreationCallback creationCallback,
ComplicationHost.InteractionCallback interactionCallback);
+
+ /**
+ * Converts a {@link com.android.settingslib.dream.DreamBackend.ComplicationType} to
+ * {@link ComplicationType}.
+ */
+ @ComplicationType
+ default int convertComplicationType(@DreamBackend.ComplicationType int type) {
+ switch (type) {
+ case DreamBackend.COMPLICATION_TYPE_TIME:
+ return COMPLICATION_TYPE_TIME;
+ case DreamBackend.COMPLICATION_TYPE_DATE:
+ return COMPLICATION_TYPE_DATE;
+ case DreamBackend.COMPLICATION_TYPE_WEATHER:
+ return COMPLICATION_TYPE_WEATHER;
+ case DreamBackend.COMPLICATION_TYPE_AIR_QUALITY:
+ return COMPLICATION_TYPE_AIR_QUALITY;
+ case DreamBackend.COMPLICATION_TYPE_CAST_INFO:
+ return COMPLICATION_TYPE_CAST_INFO;
+ default:
+ return COMPLICATION_TYPE_NONE;
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/appwidgets/AppWidgetProvider.java b/packages/SystemUI/src/com/android/systemui/dreams/appwidgets/AppWidgetProvider.java
deleted file mode 100644
index 687f7a296b1a..000000000000
--- a/packages/SystemUI/src/com/android/systemui/dreams/appwidgets/AppWidgetProvider.java
+++ /dev/null
@@ -1,101 +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.dreams.appwidgets;
-
-import android.appwidget.AppWidgetHost;
-import android.appwidget.AppWidgetHostView;
-import android.appwidget.AppWidgetManager;
-import android.appwidget.AppWidgetProviderInfo;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.res.Resources;
-import android.util.Log;
-
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.qualifiers.Main;
-
-import java.util.List;
-
-import javax.inject.Inject;
-
-/**
- * {@link AppWidgetProvider} is a singleton for accessing app widgets within SystemUI. This
- * consolidates resources such as the {@link AppWidgetHost} across potentially multiple
- * {@link ComplicationProvider} instances and other usages.
- */
-@SysUISingleton
-public class AppWidgetProvider {
- private static final String TAG = "AppWidgetProvider";
- public static final int APP_WIDGET_HOST_ID = 1025;
-
- private final Context mContext;
- private final AppWidgetManager mAppWidgetManager;
- private final AppWidgetHost mAppWidgetHost;
- private final Resources mResources;
-
- @Inject
- public AppWidgetProvider(Context context, @Main Resources resources) {
- mContext = context;
- mResources = resources;
- mAppWidgetManager = android.appwidget.AppWidgetManager.getInstance(context);
- mAppWidgetHost = new AppWidgetHost(context, APP_WIDGET_HOST_ID);
- mAppWidgetHost.startListening();
- }
-
- /**
- * Returns an {@link AppWidgetHostView} associated with a given {@link ComponentName}.
- * @param component The {@link ComponentName} of the target {@link AppWidgetHostView}.
- * @return The {@link AppWidgetHostView} or {@code null} on error.
- */
- public AppWidgetHostView getWidget(ComponentName component) {
- final List<AppWidgetProviderInfo> appWidgetInfos =
- mAppWidgetManager.getInstalledProviders();
-
- for (AppWidgetProviderInfo widgetInfo : appWidgetInfos) {
- if (widgetInfo.provider.equals(component)) {
- final int widgetId = mAppWidgetHost.allocateAppWidgetId();
-
- boolean success = mAppWidgetManager.bindAppWidgetIdIfAllowed(widgetId,
- widgetInfo.provider);
-
- if (!success) {
- Log.e(TAG, "could not bind to app widget:" + component);
- break;
- }
-
- final AppWidgetHostView appWidgetView =
- mAppWidgetHost.createView(mContext, widgetId, widgetInfo);
-
- if (appWidgetView != null) {
- // Register a layout change listener to update the widget on any sizing changes.
- appWidgetView.addOnLayoutChangeListener(
- (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
- final float density = mResources.getDisplayMetrics().density;
- final int height = Math.round((bottom - top) / density);
- final int width = Math.round((right - left) / density);
- appWidgetView.updateAppWidgetSize(null, width, height,
- width, height);
- });
- }
-
- return appWidgetView;
- }
- }
-
- return null;
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/appwidgets/ComplicationPrimer.java b/packages/SystemUI/src/com/android/systemui/dreams/appwidgets/ComplicationPrimer.java
deleted file mode 100644
index 7d30fafda8a6..000000000000
--- a/packages/SystemUI/src/com/android/systemui/dreams/appwidgets/ComplicationPrimer.java
+++ /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.dreams.appwidgets;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.res.Resources;
-import android.view.Gravity;
-
-import androidx.constraintlayout.widget.ConstraintSet;
-
-import com.android.systemui.CoreStartable;
-import com.android.systemui.R;
-import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.dreams.ComplicationHostView;
-import com.android.systemui.dreams.DreamOverlayStateController;
-import com.android.systemui.dreams.appwidgets.dagger.AppWidgetComponent;
-
-import javax.inject.Inject;
-
-/**
- * {@link ComplicationPrimer} reads the configured AppWidget Complications from resources on start
- * and populates them into the {@link DreamOverlayStateController}.
- */
-public class ComplicationPrimer extends CoreStartable {
- private final Resources mResources;
- private final DreamOverlayStateController mDreamOverlayStateController;
- private final AppWidgetComponent.Factory mComponentFactory;
-
- @Inject
- public ComplicationPrimer(Context context, @Main Resources resources,
- DreamOverlayStateController overlayStateController,
- AppWidgetComponent.Factory appWidgetOverlayFactory) {
- super(context);
- mResources = resources;
- mDreamOverlayStateController = overlayStateController;
- mComponentFactory = appWidgetOverlayFactory;
- }
-
- @Override
- public void start() {
- }
-
- @Override
- protected void onBootCompleted() {
- super.onBootCompleted();
- loadDefaultWidgets();
- }
-
- /**
- * Generates the {@link ComplicationHostView.LayoutParams} for a given gravity. Default
- * dimension constraints are also included in the params.
- * @param gravity The gravity for the layout as defined by {@link Gravity}.
- * @param resources The resourcs from which default dimensions will be extracted from.
- * @return {@link ComplicationHostView.LayoutParams} representing the provided gravity and
- * default parameters.
- */
- private static ComplicationHostView.LayoutParams getLayoutParams(int gravity,
- Resources resources) {
- final ComplicationHostView.LayoutParams params = new ComplicationHostView.LayoutParams(
- ComplicationHostView.LayoutParams.MATCH_CONSTRAINT,
- ComplicationHostView.LayoutParams.MATCH_CONSTRAINT);
-
- if ((gravity & Gravity.BOTTOM) == Gravity.BOTTOM) {
- params.bottomToBottom = ConstraintSet.PARENT_ID;
- }
-
- if ((gravity & Gravity.TOP) == Gravity.TOP) {
- params.topToTop = ConstraintSet.PARENT_ID;
- }
-
- if ((gravity & Gravity.END) == Gravity.END) {
- params.endToEnd = ConstraintSet.PARENT_ID;
- }
-
- if ((gravity & Gravity.START) == Gravity.START) {
- params.startToStart = ConstraintSet.PARENT_ID;
- }
-
- // For now, apply the same sizing constraints on every widget.
- params.matchConstraintPercentHeight =
- resources.getFloat(R.dimen.config_dreamComplicationHeightPercent);
- params.matchConstraintPercentWidth =
- resources.getFloat(R.dimen.config_dreamComplicationWidthPercent);
-
- return params;
- }
-
- /**
- * Helper method for loading widgets based on configuration.
- */
- private void loadDefaultWidgets() {
- final int[] positions = mResources.getIntArray(R.array.config_dreamComplicationPositions);
- final String[] components =
- mResources.getStringArray(R.array.config_dreamAppWidgetComplications);
-
- for (int i = 0; i < Math.min(positions.length, components.length); i++) {
- final AppWidgetComponent component = mComponentFactory.build(
- ComponentName.unflattenFromString(components[i]),
- getLayoutParams(positions[i], mResources));
-
- mDreamOverlayStateController.addComplication(
- component.getAppWidgetComplicationProvider());
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/appwidgets/ComplicationProvider.java b/packages/SystemUI/src/com/android/systemui/dreams/appwidgets/ComplicationProvider.java
deleted file mode 100644
index 9188ee54d855..000000000000
--- a/packages/SystemUI/src/com/android/systemui/dreams/appwidgets/ComplicationProvider.java
+++ /dev/null
@@ -1,83 +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.dreams.appwidgets;
-
-import android.appwidget.AppWidgetHostView;
-import android.content.ComponentName;
-import android.content.Context;
-import android.util.Log;
-import android.widget.RemoteViews;
-
-import com.android.systemui.dreams.ComplicationHost;
-import com.android.systemui.dreams.ComplicationHostView;
-import com.android.systemui.plugins.ActivityStarter;
-
-import javax.inject.Inject;
-
-/**
- * {@link ComplicationProvider} is an implementation of
- * {@link com.android.systemui.dreams.ComplicationProvider} for providing app widget-based
- * complications.
- */
-public class ComplicationProvider implements com.android.systemui.dreams.ComplicationProvider {
- private static final String TAG = "AppWidgetCompProvider";
- private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-
- private final ActivityStarter mActivityStarter;
- private final AppWidgetProvider mAppWidgetProvider;
- private final ComponentName mComponentName;
- private final ComplicationHostView.LayoutParams mLayoutParams;
-
- @Inject
- public ComplicationProvider(ActivityStarter activityStarter,
- ComponentName componentName, AppWidgetProvider widgetProvider,
- ComplicationHostView.LayoutParams layoutParams) {
- mActivityStarter = activityStarter;
- mComponentName = componentName;
- mAppWidgetProvider = widgetProvider;
- mLayoutParams = layoutParams;
- }
-
- @Override
- public void onCreateComplication(Context context,
- ComplicationHost.CreationCallback creationCallback,
- ComplicationHost.InteractionCallback interactionCallback) {
- final AppWidgetHostView widget = mAppWidgetProvider.getWidget(mComponentName);
-
- if (widget == null) {
- Log.e(TAG, "could not create widget");
- return;
- }
-
- widget.setInteractionHandler((view, pendingIntent, response) -> {
- if (pendingIntent.isActivity()) {
- if (DEBUG) {
- Log.d(TAG, "launching pending intent from app widget:" + mComponentName);
- }
- interactionCallback.onExit();
- mActivityStarter.startPendingIntentDismissingKeyguard(pendingIntent,
- null /*intentSentUiThreadCallback*/, view);
- return true;
- } else {
- return RemoteViews.startPendingIntent(view, pendingIntent,
- response.getLaunchOptions(view));
- }
- });
-
- creationCallback.onCreated(widget, mLayoutParams);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/appwidgets/dagger/AppWidgetComponent.java b/packages/SystemUI/src/com/android/systemui/dreams/appwidgets/dagger/AppWidgetComponent.java
deleted file mode 100644
index 7beed176eeca..000000000000
--- a/packages/SystemUI/src/com/android/systemui/dreams/appwidgets/dagger/AppWidgetComponent.java
+++ /dev/null
@@ -1,39 +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.dreams.appwidgets.dagger;
-
-import android.content.ComponentName;
-
-import com.android.systemui.dreams.ComplicationHostView;
-import com.android.systemui.dreams.appwidgets.ComplicationProvider;
-
-import dagger.BindsInstance;
-import dagger.Subcomponent;
-
-/** */
-@Subcomponent
-public interface AppWidgetComponent {
- /** */
- @Subcomponent.Factory
- interface Factory {
- AppWidgetComponent build(@BindsInstance ComponentName component,
- @BindsInstance ComplicationHostView.LayoutParams layoutParams);
- }
-
- /** Builds a {@link ComplicationProvider}. */
- ComplicationProvider getAppWidgetComplicationProvider();
-}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
index 0d4688ec880c..072f50db64f6 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
@@ -16,15 +16,12 @@
package com.android.systemui.dreams.dagger;
-import com.android.systemui.dreams.appwidgets.dagger.AppWidgetComponent;
-
import dagger.Module;
/**
* Dagger Module providing Communal-related functionality.
*/
@Module(subcomponents = {
- AppWidgetComponent.class,
DreamOverlayComponent.class})
public interface DreamModule {
-}
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
index 5d6c2a247df3..e24df30bfe34 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
@@ -74,9 +74,6 @@ public class Flags {
public static final ResourceBooleanFlag BOUNCER_USER_SWITCHER =
new ResourceBooleanFlag(204, R.bool.config_enableBouncerUserSwitcher);
- public static final ResourceBooleanFlag ACTIVE_UNLOCK =
- new ResourceBooleanFlag(205, R.bool.flag_active_unlock);
-
/***************************************/
// 300 - power menu
public static final BooleanFlag POWER_MENU_LITE =
@@ -88,7 +85,7 @@ public class Flags {
new BooleanFlag(400, true);
public static final BooleanFlag SMARTSPACE_SHARED_ELEMENT_TRANSITION_ENABLED =
- new BooleanFlag(401, false);
+ new BooleanFlag(401, true);
public static final ResourceBooleanFlag SMARTSPACE =
new ResourceBooleanFlag(402, R.bool.flag_smartspace);
diff --git a/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java b/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java
index b24d08d3f8bb..3ae11ff28345 100644
--- a/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java
+++ b/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java
@@ -54,7 +54,7 @@ public class FragmentHostManager {
private final View mRootView;
private final InterestingConfigChanges mConfigChanges = new InterestingConfigChanges(
ActivityInfo.CONFIG_FONT_SCALE | ActivityInfo.CONFIG_LOCALE
- | ActivityInfo.CONFIG_SCREEN_LAYOUT | ActivityInfo.CONFIG_ASSETS_PATHS);
+ | ActivityInfo.CONFIG_LAYOUT_DIRECTION | ActivityInfo.CONFIG_ASSETS_PATHS);
private final FragmentService mManager;
private final ExtensionFragmentManager mPlugins = new ExtensionFragmentManager();
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
index 69bcf2ec8b8d..582965a12528 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
@@ -21,6 +21,8 @@ import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
import android.content.Context
import android.graphics.Matrix
+import android.graphics.Rect
+import android.os.Handler
import android.view.RemoteAnimationTarget
import android.view.SyncRtSurfaceTransactionApplier
import android.view.View
@@ -33,11 +35,17 @@ import com.android.systemui.animation.Interpolators
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
-import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController
+import com.android.systemui.plugins.BcSmartspaceDataPlugin
+import com.android.systemui.shared.system.ActivityManagerWrapper
+import com.android.systemui.shared.system.QuickStepContract
+import com.android.systemui.shared.system.smartspace.ILauncherUnlockAnimationController
+import com.android.systemui.shared.system.smartspace.ISysuiUnlockAnimationController
+import com.android.systemui.shared.system.smartspace.SmartspaceState
import com.android.systemui.statusbar.phone.BiometricUnlockController
import com.android.systemui.statusbar.policy.KeyguardStateController
import dagger.Lazy
import javax.inject.Inject
+import kotlin.math.min
/**
* Starting scale factor for the app/launcher surface behind the keyguard, when it's animating
@@ -76,6 +84,25 @@ const val DISMISS_AMOUNT_SHOW_SURFACE_THRESHOLD = 0.25f
const val DISMISS_AMOUNT_EXIT_KEYGUARD_THRESHOLD = 0.4f
/**
+ * How long the canned unlock animation takes. This is used if we are unlocking from biometric auth,
+ * from a tap on the unlock icon, or from the bouncer. This is not relevant if the lockscreen is
+ * swiped away via a touch gesture, or when it's flinging expanded/collapsed after a swipe.
+ */
+const val UNLOCK_ANIMATION_DURATION_MS = 200L
+
+/**
+ * Duration for the alpha animation on the surface behind. This plays to fade in the surface during
+ * a swipe to unlock (and to fade it back out if the swipe is cancelled).
+ */
+const val SURFACE_BEHIND_SWIPE_FADE_DURATION_MS = 150L
+
+/**
+ * Start delay for the surface behind animation, used so that the lockscreen can get out of the way
+ * before the surface begins appearing.
+ */
+const val UNLOCK_ANIMATION_SURFACE_BEHIND_START_DELAY_MS = 75L
+
+/**
* Initiates, controls, and ends the keyguard unlock animation.
*
* The unlock animation transitions between the keyguard (lock screen) and the app/launcher surface
@@ -85,7 +112,7 @@ const val DISMISS_AMOUNT_EXIT_KEYGUARD_THRESHOLD = 0.4f
* this controller will play a canned animation on the surface as well.
*
* The surface behind the keyguard is manipulated via a RemoteAnimation passed to
- * [notifyStartKeyguardExitAnimation] by [KeyguardViewMediator].
+ * [notifyStartSurfaceBehindRemoteAnimation] by [KeyguardViewMediator].
*/
@SysUISingleton
class KeyguardUnlockAnimationController @Inject constructor(
@@ -94,10 +121,99 @@ class KeyguardUnlockAnimationController @Inject constructor(
private val
keyguardViewMediator: Lazy<KeyguardViewMediator>,
private val keyguardViewController: KeyguardViewController,
- private val smartspaceTransitionController: SmartspaceTransitionController,
private val featureFlags: FeatureFlags,
- private val biometricUnlockController: BiometricUnlockController
-) : KeyguardStateController.Callback {
+ private val biometricUnlockControllerLazy: Lazy<BiometricUnlockController>
+) : KeyguardStateController.Callback, ISysuiUnlockAnimationController.Stub() {
+
+ interface KeyguardUnlockAnimationListener {
+ /**
+ * Called when the remote unlock animation, controlled by
+ * [KeyguardUnlockAnimationController], first starts.
+ *
+ * [playingCannedAnimation] indicates whether we are playing a canned animation to show the
+ * app/launcher behind the keyguard, vs. this being a swipe to unlock where the dismiss
+ * amount drives the animation.
+ * [fromWakeAndUnlock] tells us whether we are unlocking directly from AOD - in this case,
+ * the lockscreen is dismissed instantly, so we shouldn't run any animations that rely on it
+ * being visible.
+ */
+ @JvmDefault
+ fun onUnlockAnimationStarted(playingCannedAnimation: Boolean, fromWakeAndUnlock: Boolean) {}
+
+ /**
+ * Called when the remote unlock animation ends, in all cases, canned or swipe-to-unlock.
+ * The keyguard is no longer visible in this state and the app/launcher behind the keyguard
+ * is now completely visible.
+ */
+ @JvmDefault
+ fun onUnlockAnimationFinished() {}
+
+ /**
+ * Called when we begin the smartspace shared element transition, either due to an unlock
+ * action (biometric, etc.) or a swipe to unlock.
+ *
+ * This transition can begin BEFORE [onUnlockAnimationStarted] is called, if we are swiping
+ * to unlock and the surface behind the keyguard has not yet been made visible. This is
+ * because the lockscreen smartspace immediately begins moving towards the launcher
+ * smartspace location when a swipe begins, even before we start the keyguard exit remote
+ * animation and show the launcher itself.
+ */
+ @JvmDefault
+ fun onSmartspaceSharedElementTransitionStarted() {}
+ }
+
+ /** The SmartSpace view on the lockscreen, provided by [KeyguardClockSwitchController]. */
+ var lockscreenSmartspace: View? = null
+
+ /**
+ * The state of the Launcher's smartspace, delivered via [onLauncherSmartspaceStateUpdated].
+ * This is pushed to us from Launcher whenever their smartspace moves or its visibility changes.
+ * We'll animate the lockscreen smartspace to this location during an unlock.
+ */
+ var launcherSmartspaceState: SmartspaceState? = null
+
+ /**
+ * Whether a canned unlock animation is playing, vs. currently unlocking in response to a swipe
+ * gesture or panel fling. If we're swiping/flinging, the unlock animation is driven by the
+ * dismiss amount, via [onKeyguardDismissAmountChanged]. If we're using a canned animation, it's
+ * being driven by ValueAnimators started in [playCannedUnlockAnimation].
+ */
+ var playingCannedUnlockAnimation = false
+
+ /**
+ * Remote callback provided by Launcher that allows us to control the Launcher's unlock
+ * animation and smartspace.
+ *
+ * If this is null, we will not be animating any Launchers today and should fall back to window
+ * animations.
+ */
+ private var launcherUnlockController: ILauncherUnlockAnimationController? = null
+
+ private val listeners = ArrayList<KeyguardUnlockAnimationListener>()
+
+ /**
+ * Called from SystemUiProxy to pass us the launcher's unlock animation controller. If this
+ * doesn't happen, we won't use in-window animations or the smartspace shared element
+ * transition, but that's okay!
+ */
+ override fun setLauncherUnlockController(callback: ILauncherUnlockAnimationController?) {
+ launcherUnlockController = callback
+
+ // If the provided callback dies, set it to null. We'll always check whether it's null
+ // to avoid DeadObjectExceptions.
+ callback?.asBinder()?.linkToDeath({
+ launcherUnlockController = null
+ launcherSmartspaceState = null
+ }, 0 /* flags */)
+ }
+
+ /**
+ * Called from SystemUiProxy to pass us the latest state of the Launcher's smartspace. This is
+ * only done when the state has changed in some way.
+ */
+ override fun onLauncherSmartspaceStateUpdated(state: SmartspaceState?) {
+ launcherSmartspaceState = state
+ }
/**
* Information used to start, run, and finish a RemoteAnimation on the app or launcher surface
@@ -105,15 +221,16 @@ class KeyguardUnlockAnimationController @Inject constructor(
*
* If we're swiping to unlock, the "animation" is controlled via the gesture, tied to the
* dismiss amounts received in [onKeyguardDismissAmountChanged]. It does not have a fixed
- * duration, and it ends when the gesture reaches a certain threshold or is cancelled.
+ * duration, and it ends when the gesture reaches a certain threshold or is cancell
*
* If we're unlocking via biometrics, PIN entry, or from clicking a notification, a canned
- * animation is started in [notifyStartKeyguardExitAnimation].
+ * animation is started in [playCannedUnlockAnimation].
*/
@VisibleForTesting
var surfaceTransactionApplier: SyncRtSurfaceTransactionApplier? = null
private var surfaceBehindRemoteAnimationTarget: RemoteAnimationTarget? = null
private var surfaceBehindRemoteAnimationStartTime: Long = 0
+ private var surfaceBehindParams: SyncRtSurfaceTransactionApplier.SurfaceParams? = null
/**
* Alpha value applied to [surfaceBehindRemoteAnimationTarget], which is the surface of the
@@ -125,6 +242,7 @@ class KeyguardUnlockAnimationController @Inject constructor(
*/
private var surfaceBehindAlpha = 1f
private var surfaceBehindAlphaAnimator = ValueAnimator.ofFloat(0f, 1f)
+ private var smartspaceAnimator = ValueAnimator.ofFloat(0f, 1f)
/**
* Matrix applied to [surfaceBehindRemoteAnimationTarget], which is the surface of the
@@ -144,57 +262,113 @@ class KeyguardUnlockAnimationController @Inject constructor(
/** Rounded corner radius to apply to the surface behind the keyguard. */
private var roundedCornerRadius = 0f
- /** The SmartSpace view on the lockscreen, provided by [KeyguardClockSwitchController]. */
- public var lockscreenSmartSpace: View? = null
+ /**
+ * Whether we tried to start the SmartSpace shared element transition for this unlock swipe.
+ * It's possible we were unable to do so (if the Launcher SmartSpace is not available), and we
+ * need to keep track of that so that we don't start doing it halfway through the swipe if
+ * Launcher becomes available suddenly.
+ */
+ private var attemptedSmartSpaceTransitionForThisSwipe = false
/**
- * Whether we are currently in the process of unlocking the keyguard, and we are performing the
- * shared element SmartSpace transition.
+ * The original location of the lockscreen smartspace on the screen.
*/
- private var unlockingWithSmartSpaceTransition: Boolean = false
+ private val smartspaceOriginBounds = Rect()
/**
- * Whether we tried to start the SmartSpace shared element transition for this unlock swipe.
- * It's possible we're unable to do so (if the Launcher SmartSpace is not available).
+ * The bounds to which the lockscreen smartspace is moving. This is set to the bounds of the
+ * launcher's smartspace prior to the transition starting.
*/
- private var attemptedSmartSpaceTransitionForThisSwipe = false
+ private val smartspaceDestBounds = Rect()
+
+ /**
+ * From 0f to 1f, the progress of the smartspace shared element animation. 0f means the
+ * smartspace is at its normal position within the lock screen hierarchy, and 1f means it has
+ * fully animated to the location of the Launcher's smartspace.
+ */
+ private var smartspaceUnlockProgress = 0f
+
+ /**
+ * Whether we're currently unlocking, and we're talking to Launcher to perform in-window
+ * animations rather than simply animating the Launcher window like any other app. This can be
+ * true while [unlockingWithSmartspaceTransition] is false, if the smartspace is not available
+ * or was not ready in time.
+ */
+ private var unlockingToLauncherWithInWindowAnimations: Boolean = false
+
+ /**
+ * Whether we are currently unlocking, and the smartspace shared element transition is in
+ * progress. If true, we're also [unlockingToLauncherWithInWindowAnimations].
+ */
+ private var unlockingWithSmartspaceTransition: Boolean = false
+
+ private val handler = Handler()
init {
- surfaceBehindAlphaAnimator.duration = 150
- surfaceBehindAlphaAnimator.interpolator = Interpolators.ALPHA_IN
- surfaceBehindAlphaAnimator.addUpdateListener { valueAnimator: ValueAnimator ->
- surfaceBehindAlpha = valueAnimator.animatedValue as Float
- updateSurfaceBehindAppearAmount()
- }
- surfaceBehindAlphaAnimator.addListener(object : AnimatorListenerAdapter() {
- override fun onAnimationEnd(animation: Animator) {
- // If the surface alpha is 0f, it's no longer visible so we can safely be done with
- // the animation.
- if (surfaceBehindAlpha == 0f) {
- keyguardViewMediator.get().finishSurfaceBehindRemoteAnimation(
+ with(surfaceBehindAlphaAnimator) {
+ duration = SURFACE_BEHIND_SWIPE_FADE_DURATION_MS
+ interpolator = Interpolators.TOUCH_RESPONSE
+ addUpdateListener { valueAnimator: ValueAnimator ->
+ surfaceBehindAlpha = valueAnimator.animatedValue as Float
+ updateSurfaceBehindAppearAmount()
+ }
+ addListener(object : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animation: Animator) {
+ // If the surface alpha is 0f, it's no longer visible so we can safely be done
+ // with the animation even if other properties are still animating.
+ if (surfaceBehindAlpha == 0f) {
+ keyguardViewMediator.get().finishSurfaceBehindRemoteAnimation(
false /* cancelled */)
+ }
}
- }
- })
+ })
+ }
- surfaceBehindEntryAnimator.duration = 450
- surfaceBehindEntryAnimator.interpolator = Interpolators.DECELERATE_QUINT
- surfaceBehindEntryAnimator.addUpdateListener { valueAnimator: ValueAnimator ->
- surfaceBehindAlpha = valueAnimator.animatedValue as Float
- setSurfaceBehindAppearAmount(valueAnimator.animatedValue as Float)
+ with(surfaceBehindEntryAnimator) {
+ duration = UNLOCK_ANIMATION_DURATION_MS
+ startDelay = UNLOCK_ANIMATION_SURFACE_BEHIND_START_DELAY_MS
+ interpolator = Interpolators.TOUCH_RESPONSE
+ addUpdateListener { valueAnimator: ValueAnimator ->
+ surfaceBehindAlpha = valueAnimator.animatedValue as Float
+ setSurfaceBehindAppearAmount(valueAnimator.animatedValue as Float)
+ }
+ addListener(object : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animation: Animator) {
+ playingCannedUnlockAnimation = false
+ keyguardViewMediator.get().onKeyguardExitRemoteAnimationFinished(
+ false /* cancelled */
+ )
+ }
+ })
}
- surfaceBehindEntryAnimator.addListener(object : AnimatorListenerAdapter() {
- override fun onAnimationEnd(animation: Animator) {
- keyguardViewMediator.get().onKeyguardExitRemoteAnimationFinished(
- false /* cancelled */)
+
+ with(smartspaceAnimator) {
+ duration = UNLOCK_ANIMATION_DURATION_MS
+ interpolator = Interpolators.TOUCH_RESPONSE
+ addUpdateListener {
+ smartspaceUnlockProgress = it.animatedValue as Float
}
- })
+ addListener(object : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animation: Animator?) {
+ launcherUnlockController?.setSmartspaceVisibility(View.VISIBLE)
+ keyguardViewMediator.get().onKeyguardExitRemoteAnimationFinished(
+ false /* cancelled */)
+ }
+ })
+ }
// Listen for changes in the dismiss amount.
keyguardStateController.addCallback(this)
roundedCornerRadius =
- context.resources.getDimensionPixelSize(R.dimen.rounded_corner_radius).toFloat()
+ context.resources.getDimensionPixelSize(R.dimen.rounded_corner_radius).toFloat()
+ }
+
+ /**
+ * Add a listener to be notified of various stages of the unlock animation.
+ */
+ fun addKeyguardUnlockAnimationListener(listener: KeyguardUnlockAnimationListener) {
+ listeners.add(listener)
}
/**
@@ -203,78 +377,220 @@ class KeyguardUnlockAnimationController @Inject constructor(
* surface for the unlock gesture/animation.
*
* When we're done with it, we'll call [KeyguardViewMediator.finishSurfaceBehindRemoteAnimation]
- * to end the RemoteAnimation.
+ * to end the RemoteAnimation. The KeyguardViewMediator will then end the animation and let us
+ * know that it's over by calling [notifyFinishedKeyguardExitAnimation].
*
- * [requestedShowSurfaceBehindKeyguard] denotes whether the exit animation started because of a
+ * [requestedShowSurfaceBehindKeyguard] indicates whether the animation started because of a
* call to [KeyguardViewMediator.showSurfaceBehindKeyguard], as happens during a swipe gesture,
- * as opposed to the keyguard hiding.
+ * as opposed to being called because the device was unlocked and the keyguard is going away.
*/
- fun notifyStartKeyguardExitAnimation(
+ fun notifyStartSurfaceBehindRemoteAnimation(
target: RemoteAnimationTarget,
startTime: Long,
requestedShowSurfaceBehindKeyguard: Boolean
) {
-
if (surfaceTransactionApplier == null) {
surfaceTransactionApplier = SyncRtSurfaceTransactionApplier(
keyguardViewController.viewRootImpl.view)
}
+ // New animation, new params.
+ surfaceBehindParams = null
+
surfaceBehindRemoteAnimationTarget = target
surfaceBehindRemoteAnimationStartTime = startTime
- // If the surface behind wasn't made visible during a swipe, we'll do a canned animation
- // to animate it in. Otherwise, the swipe touch events will continue animating it.
+ // If we specifically requested that the surface behind be made visible, it means we are
+ // swiping to unlock. In that case, the surface visibility is tied to the dismiss amount,
+ // and we'll handle that in onKeyguardDismissAmountChanged(). If we didn't request that, the
+ // keyguard is being dismissed for a different reason (biometric auth, etc.) and we should
+ // play a canned animation to make the surface fully visible.
if (!requestedShowSurfaceBehindKeyguard) {
- keyguardViewController.hide(startTime, 350)
-
- // If we're wake and unlocking, we don't want to animate the surface since we're going
- // to do the light reveal scrim from the black AOD screen. Make it visible and end the
- // remote aimation.
- if (biometricUnlockController.isWakeAndUnlock) {
- setSurfaceBehindAppearAmount(1f)
- keyguardViewMediator.get().onKeyguardExitRemoteAnimationFinished(
- false /* cancelled */)
- } else {
- // Otherwise, animate it in normally.
- surfaceBehindEntryAnimator.start()
- }
+ playCannedUnlockAnimation()
}
+ listeners.forEach {
+ it.onUnlockAnimationStarted(
+ playingCannedUnlockAnimation /* playingCannedAnimation */,
+ biometricUnlockControllerLazy.get().isWakeAndUnlock /* isWakeAndUnlock */) }
+
// Finish the keyguard remote animation if the dismiss amount has crossed the threshold.
// Check it here in case there is no more change to the dismiss amount after the last change
// that starts the keyguard animation. @see #updateKeyguardViewMediatorIfThresholdsReached()
finishKeyguardExitRemoteAnimationIfReachThreshold()
}
- fun notifyCancelKeyguardExitAnimation() {
+ /**
+ * Called by [KeyguardViewMediator] to let us know that the remote animation has finished, and
+ * we should clean up all of our state.
+ */
+ fun notifyFinishedKeyguardExitAnimation(cancelled: Boolean) {
+ // Cancel any pending actions.
+ handler.removeCallbacksAndMessages(null)
+
+ // Make sure we made the surface behind fully visible, just in case. It should already be
+ // fully visible.
+ setSurfaceBehindAppearAmount(1f)
+ launcherUnlockController?.setUnlockAmount(1f)
+ smartspaceDestBounds.setEmpty()
+
+ // That target is no longer valid since the animation finished, null it out.
surfaceBehindRemoteAnimationTarget = null
+ surfaceBehindParams = null
+
+ playingCannedUnlockAnimation = false
+ unlockingToLauncherWithInWindowAnimations = false
+ unlockingWithSmartspaceTransition = false
+ resetSmartspaceTransition()
+
+ listeners.forEach { it.onUnlockAnimationFinished() }
}
- fun notifyFinishedKeyguardExitAnimation() {
- surfaceBehindRemoteAnimationTarget = null
+ /**
+ * Play a canned unlock animation to unlock the device. This is used when we were *not* swiping
+ * to unlock using a touch gesture. If we were swiping to unlock, the animation will be driven
+ * by the dismiss amount via [onKeyguardDismissAmountChanged].
+ */
+ fun playCannedUnlockAnimation() {
+ playingCannedUnlockAnimation = true
+
+ if (canPerformInWindowLauncherAnimations()) {
+ // If possible, use the neat in-window animations to unlock to the launcher.
+ unlockToLauncherWithInWindowAnimations()
+ } else if (!biometricUnlockControllerLazy.get().isWakeAndUnlock) {
+ // If the launcher isn't behind the keyguard, or the launcher unlock controller is not
+ // available, animate in the entire window.
+ surfaceBehindEntryAnimator.start()
+ } else {
+ setSurfaceBehindAppearAmount(1f)
+ keyguardViewMediator.get().onKeyguardExitRemoteAnimationFinished(false)
+ }
+
+ // If this is a wake and unlock, hide the lockscreen immediately. In the future, we should
+ // animate it out nicely instead, but to the current state of wake and unlock, not hiding it
+ // causes a lot of issues.
+ // TODO(b/210016643): Not this, it looks not-ideal!
+ if (biometricUnlockControllerLazy.get().isWakeAndUnlock) {
+ keyguardViewController.hide(surfaceBehindRemoteAnimationStartTime, 350)
+ }
}
- fun hideKeyguardViewAfterRemoteAnimation() {
- keyguardViewController.hide(surfaceBehindRemoteAnimationStartTime, 350)
+ /**
+ * Unlock to the launcher, using in-window animations, and the smartspace shared element
+ * transition if possible.
+ */
+ private fun unlockToLauncherWithInWindowAnimations() {
+ unlockingToLauncherWithInWindowAnimations = true
+
+ // See if we can do the smartspace transition, and if so, do it!
+ if (prepareForSmartspaceTransition()) {
+ animateSmartspaceToDestination()
+ listeners.forEach { it.onSmartspaceSharedElementTransitionStarted() }
+ }
+
+ // Tell the launcher to prepare for the animation by setting its views invisible and
+ // syncing the selected smartspace pages.
+ launcherUnlockController?.prepareForUnlock(
+ unlockingWithSmartspaceTransition /* willAnimateSmartspace */,
+ (lockscreenSmartspace as BcSmartspaceDataPlugin.SmartspaceView?)?.selectedPage ?: -1)
+
+ // Begin the animation.
+ launcherUnlockController?.playUnlockAnimation(
+ true /* unlocked */, UNLOCK_ANIMATION_DURATION_MS)
+ if (!unlockingWithSmartspaceTransition) {
+ // If we are not unlocking with the smartspace transition, wait for the unlock animation
+ // to end and then finish the remote animation. If we are using the smartspace
+ // transition, it will finish the remote animation once it ends.
+ handler.postDelayed({
+ keyguardViewMediator.get().onKeyguardExitRemoteAnimationFinished(
+ false /* cancelled */)
+ }, UNLOCK_ANIMATION_DURATION_MS)
+ }
+
+ // Wait a moment, then show the launcher surface.
+ setSurfaceBehindAppearAmount(1f)
}
/**
- * Whether we are currently in the process of unlocking the keyguard, and we are performing the
- * shared element SmartSpace transition.
+ * Animates the lockscreen smartspace all the way to the launcher's smartspace location, then
+ * makes the launcher smartspace visible and ends the remote animation.
*/
- fun isUnlockingWithSmartSpaceTransition(): Boolean {
- return unlockingWithSmartSpaceTransition
+ private fun animateSmartspaceToDestination() {
+ smartspaceAnimator.start()
+ }
+
+ /**
+ * Reset the lockscreen smartspace's position, and reset all state involving the smartspace
+ * transition.
+ */
+ public fun resetSmartspaceTransition() {
+ unlockingWithSmartspaceTransition = false
+ smartspaceUnlockProgress = 0f
+
+ lockscreenSmartspace?.post {
+ lockscreenSmartspace!!.translationX = 0f
+ lockscreenSmartspace!!.translationY = 0f
+ }
+ }
+
+ /**
+ * Moves the lockscreen smartspace towards the launcher smartspace's position.
+ */
+ private fun setSmartspaceProgressToDestinationBounds(progress: Float) {
+ if (smartspaceDestBounds.isEmpty) {
+ return
+ }
+
+ val progressClamped = min(1f, progress)
+
+ // Calculate the distance (relative to the origin) that we need to be for the current
+ // progress value.
+ val progressX =
+ (smartspaceDestBounds.left - smartspaceOriginBounds.left) * progressClamped
+ val progressY =
+ (smartspaceDestBounds.top - smartspaceOriginBounds.top) * progressClamped
+
+ val lockscreenSmartspaceCurrentBounds = Rect().also {
+ lockscreenSmartspace!!.getBoundsOnScreen(it)
+ }
+
+ // Figure out how far that is from our present location on the screen. This approach
+ // compensates for the fact that our parent container is also translating to animate out.
+ val dx = smartspaceOriginBounds.left + progressX -
+ lockscreenSmartspaceCurrentBounds.left
+ val dy = smartspaceOriginBounds.top + progressY -
+ lockscreenSmartspaceCurrentBounds.top
+
+ with(lockscreenSmartspace!!) {
+ translationX += dx
+ translationY += dy
+ }
}
/**
* Update the lockscreen SmartSpace to be positioned according to the current dismiss amount. As
* the dismiss amount increases, we will increase our SmartSpace's progress to the destination
* bounds (the location of the Launcher SmartSpace).
+ *
+ * This is used by [KeyguardClockSwitchController] to keep the smartspace position updated as
+ * the clock is swiped away.
*/
fun updateLockscreenSmartSpacePosition() {
- smartspaceTransitionController.setProgressToDestinationBounds(
- keyguardStateController.dismissAmount / DISMISS_AMOUNT_EXIT_KEYGUARD_THRESHOLD)
+ setSmartspaceProgressToDestinationBounds(smartspaceUnlockProgress)
+ }
+
+ /**
+ * Asks the keyguard view to hide, using the start time from the beginning of the remote
+ * animation.
+ */
+ fun hideKeyguardViewAfterRemoteAnimation() {
+ // Hide the keyguard, with no fade out since we animated it away during the unlock.
+ keyguardViewController.hide(surfaceBehindRemoteAnimationStartTime, 0 /* fadeOutDuration */)
+ }
+
+ private fun applyParamsToSurface(params: SyncRtSurfaceTransactionApplier.SurfaceParams) {
+ surfaceTransactionApplier!!.scheduleApply(params)
+ surfaceBehindParams = params
}
/**
@@ -287,34 +603,56 @@ class KeyguardUnlockAnimationController @Inject constructor(
return
}
- val surfaceHeight: Int = surfaceBehindRemoteAnimationTarget!!.screenSpaceBounds.height()
- val scaleFactor = (SURFACE_BEHIND_START_SCALE_FACTOR +
- (1f - SURFACE_BEHIND_START_SCALE_FACTOR) *
- MathUtils.clamp(amount, 0f, 1f))
+ if (unlockingToLauncherWithInWindowAnimations) {
+ // If we're using the in-window launcher animations, and haven't yet applied alpha = 1f
+ // to the launcher surface, do that now so we can see the launcher animations.
+ if (surfaceBehindParams?.alpha?.let { it < 1f } != false) {
+ applyParamsToSurface(
+ SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(
+ surfaceBehindRemoteAnimationTarget!!.leash)
+ .withAlpha(1f)
+ .build())
+ }
- // Scale up from a point at the center-bottom of the surface.
- surfaceBehindMatrix.setScale(
+ // If we aren't using the canned unlock animation (which would be setting the unlock
+ // amount in its update listener), do it here.
+ if (!isPlayingCannedUnlockAnimation()) {
+ launcherUnlockController?.setUnlockAmount(amount)
+ }
+ } else {
+ // Otherwise, animate in the surface's scale/transltion.
+ val surfaceHeight: Int = surfaceBehindRemoteAnimationTarget!!.screenSpaceBounds.height()
+ val scaleFactor = (SURFACE_BEHIND_START_SCALE_FACTOR +
+ (1f - SURFACE_BEHIND_START_SCALE_FACTOR) *
+ MathUtils.clamp(amount, 0f, 1f))
+
+ // Scale up from a point at the center-bottom of the surface.
+ surfaceBehindMatrix.setScale(
scaleFactor,
scaleFactor,
surfaceBehindRemoteAnimationTarget!!.screenSpaceBounds.width() / 2f,
- surfaceHeight * SURFACE_BEHIND_SCALE_PIVOT_Y)
+ surfaceHeight * SURFACE_BEHIND_SCALE_PIVOT_Y
+ )
- // Translate up from the bottom.
- surfaceBehindMatrix.postTranslate(0f,
- surfaceHeight * SURFACE_BEHIND_START_TRANSLATION_Y * (1f - amount))
+ // Translate up from the bottom.
+ surfaceBehindMatrix.postTranslate(
+ 0f,
+ surfaceHeight * SURFACE_BEHIND_START_TRANSLATION_Y * (1f - amount)
+ )
- // If we're snapping the keyguard back, immediately begin fading it out.
- val animationAlpha =
+ // If we're snapping the keyguard back, immediately begin fading it out.
+ val animationAlpha =
if (keyguardStateController.isSnappingKeyguardBackAfterSwipe) amount
else surfaceBehindAlpha
- val params = SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(
- surfaceBehindRemoteAnimationTarget!!.leash)
+ applyParamsToSurface(
+ SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(
+ surfaceBehindRemoteAnimationTarget!!.leash)
.withMatrix(surfaceBehindMatrix)
.withCornerRadius(roundedCornerRadius)
.withAlpha(animationAlpha)
- .build()
- surfaceTransactionApplier!!.scheduleApply(params)
+ .build())
+ }
}
/**
@@ -326,6 +664,10 @@ class KeyguardUnlockAnimationController @Inject constructor(
return
}
+ if (playingCannedUnlockAnimation) {
+ return
+ }
+
// For fling animations, we want to animate the surface in over the full distance. If we're
// dismissing the keyguard via a swipe gesture (or cancelling the swipe gesture), we want to
// bring in the surface behind over a relatively short swipe distance (~15%), to keep the
@@ -344,17 +686,19 @@ class KeyguardUnlockAnimationController @Inject constructor(
}
override fun onKeyguardDismissAmountChanged() {
- if (!KeyguardService.sEnableRemoteKeyguardGoingAwayAnimation) {
+ if (!willHandleUnlockAnimation()) {
return
}
if (keyguardViewController.isShowing) {
- updateKeyguardViewMediatorIfThresholdsReached()
+ showOrHideSurfaceIfDismissAmountThresholdsReached()
// If the surface is visible or it's about to be, start updating its appearance to
// reflect the new dismiss amount.
- if (keyguardViewMediator.get().requestedShowSurfaceBehindKeyguard() ||
- keyguardViewMediator.get().isAnimatingBetweenKeyguardAndSurfaceBehindOrWillBe) {
+ if ((keyguardViewMediator.get().requestedShowSurfaceBehindKeyguard() ||
+ keyguardViewMediator.get()
+ .isAnimatingBetweenKeyguardAndSurfaceBehindOrWillBe) &&
+ !playingCannedUnlockAnimation) {
updateSurfaceBehindAppearAmount()
}
}
@@ -362,7 +706,7 @@ class KeyguardUnlockAnimationController @Inject constructor(
// The end of the SmartSpace transition can occur after the keyguard is hidden (when we tell
// Launcher's SmartSpace to become visible again), so update it even if the keyguard view is
// no longer showing.
- updateSmartSpaceTransition()
+ applyDismissAmountToSmartspaceTransition()
}
/**
@@ -370,16 +714,28 @@ class KeyguardUnlockAnimationController @Inject constructor(
* such as reaching the point in the dismiss swipe where we need to make the surface behind the
* keyguard visible.
*/
- private fun updateKeyguardViewMediatorIfThresholdsReached() {
+ private fun showOrHideSurfaceIfDismissAmountThresholdsReached() {
if (!featureFlags.isEnabled(Flags.NEW_UNLOCK_SWIPE_ANIMATION)) {
return
}
+ // If we are playing the canned unlock animation, we flung away the keyguard to hide it and
+ // started a canned animation to show the surface behind the keyguard. The fling will cause
+ // panel height/dismiss amount updates, but we should ignore those updates here since the
+ // surface behind is already visible and animating.
+ if (playingCannedUnlockAnimation) {
+ return
+ }
+
val dismissAmount = keyguardStateController.dismissAmount
if (dismissAmount >= DISMISS_AMOUNT_SHOW_SURFACE_THRESHOLD &&
!keyguardViewMediator.get().requestedShowSurfaceBehindKeyguard()) {
// We passed the threshold, and we're not yet showing the surface behind the
// keyguard. Animate it in.
+ if (canPerformInWindowLauncherAnimations()) {
+ launcherUnlockController?.setUnlockAmount(0f)
+ unlockingToLauncherWithInWindowAnimations = true
+ }
keyguardViewMediator.get().showSurfaceBehindKeyguard()
fadeInSurfaceBehind()
} else if (dismissAmount < DISMISS_AMOUNT_SHOW_SURFACE_THRESHOLD &&
@@ -388,9 +744,9 @@ class KeyguardUnlockAnimationController @Inject constructor(
// out.
keyguardViewMediator.get().hideSurfaceBehindKeyguard()
fadeOutSurfaceBehind()
- } else {
- finishKeyguardExitRemoteAnimationIfReachThreshold()
}
+
+ finishKeyguardExitRemoteAnimationIfReachThreshold()
}
/**
@@ -417,6 +773,7 @@ class KeyguardUnlockAnimationController @Inject constructor(
// animating it out. This will be called again after the fling ends.
!keyguardStateController.isFlingingToDismissKeyguardDuringSwipeGesture &&
dismissAmount >= DISMISS_AMOUNT_EXIT_KEYGUARD_THRESHOLD)) {
+ setSurfaceBehindAppearAmount(1f)
keyguardViewMediator.get().onKeyguardExitRemoteAnimationFinished(false /* cancelled */)
}
}
@@ -426,31 +783,53 @@ class KeyguardUnlockAnimationController @Inject constructor(
* dismiss amount, and also updates the SmartSpaceTransitionController, which will let Launcher
* know if it needs to do something as a result.
*/
- private fun updateSmartSpaceTransition() {
+ private fun applyDismissAmountToSmartspaceTransition() {
if (!featureFlags.isEnabled(Flags.SMARTSPACE_SHARED_ELEMENT_TRANSITION_ENABLED)) {
return
}
+ // If we are playing the canned animation, the smartspace is being animated directly between
+ // its original location and the location of the launcher smartspace by smartspaceAnimator.
+ // We can ignore the dismiss amount, which is caused by panel height changes as the panel is
+ // flung away.
+ if (playingCannedUnlockAnimation) {
+ return
+ }
+
val dismissAmount = keyguardStateController.dismissAmount
- // If we've begun a swipe, and are capable of doing the SmartSpace transition, start it!
+ // If we've begun a swipe, and haven't yet tried doing the SmartSpace transition, do that
+ // now.
if (!attemptedSmartSpaceTransitionForThisSwipe &&
- dismissAmount > 0f &&
- dismissAmount < 1f &&
- keyguardViewController.isShowing) {
+ keyguardViewController.isShowing &&
+ dismissAmount > 0f &&
+ dismissAmount < 1f) {
attemptedSmartSpaceTransitionForThisSwipe = true
- smartspaceTransitionController.prepareForUnlockTransition()
- if (keyguardStateController.canPerformSmartSpaceTransition()) {
- unlockingWithSmartSpaceTransition = true
- smartspaceTransitionController.launcherSmartspace?.setVisibility(
- View.INVISIBLE)
+ if (prepareForSmartspaceTransition()) {
+ unlockingWithSmartspaceTransition = true
+
+ // Ensure that the smartspace is invisible if we're doing the transition, and
+ // visible if we aren't.
+ launcherUnlockController?.setSmartspaceVisibility(
+ if (unlockingWithSmartspaceTransition) View.INVISIBLE else View.VISIBLE)
+
+ if (unlockingWithSmartspaceTransition) {
+ listeners.forEach { it.onSmartspaceSharedElementTransitionStarted() }
+ }
}
} else if (attemptedSmartSpaceTransitionForThisSwipe &&
- (dismissAmount == 0f || dismissAmount == 1f)) {
+ (dismissAmount == 0f || dismissAmount == 1f)) {
attemptedSmartSpaceTransitionForThisSwipe = false
- unlockingWithSmartSpaceTransition = false
- smartspaceTransitionController.launcherSmartspace?.setVisibility(View.VISIBLE)
+ unlockingWithSmartspaceTransition = false
+ launcherUnlockController?.setSmartspaceVisibility(View.VISIBLE)
+ }
+
+ if (unlockingWithSmartspaceTransition) {
+ val swipedFraction: Float = keyguardStateController.dismissAmount
+ val progress = swipedFraction / DISMISS_AMOUNT_EXIT_KEYGUARD_THRESHOLD
+ smartspaceUnlockProgress = progress
+ setSmartspaceProgressToDestinationBounds(smartspaceUnlockProgress)
}
}
@@ -463,4 +842,121 @@ class KeyguardUnlockAnimationController @Inject constructor(
surfaceBehindAlphaAnimator.cancel()
surfaceBehindAlphaAnimator.reverse()
}
+
+ /**
+ * Prepare for the smartspace shared element transition, if possible, by figuring out where we
+ * are animating from/to.
+ *
+ * Return true if we'll be able to do the smartspace transition, or false if conditions are not
+ * right to do it right now.
+ */
+ private fun prepareForSmartspaceTransition(): Boolean {
+ // Feature is disabled, so we don't want to.
+ if (!featureFlags.isEnabled(Flags.SMARTSPACE_SHARED_ELEMENT_TRANSITION_ENABLED)) {
+ return false
+ }
+
+ // If our controllers are null, or we haven't received a smartspace state from Launcher yet,
+ // we will not be doing any smartspace transitions today.
+ if (launcherUnlockController == null ||
+ lockscreenSmartspace == null ||
+ launcherSmartspaceState == null) {
+ return false
+ }
+
+ // If the launcher does not have a visible smartspace (either because it's paged off-screen,
+ // or the smartspace just doesn't exist), we can't do the transition.
+ if ((launcherSmartspaceState?.visibleOnScreen) != true) {
+ return false
+ }
+
+ // If our launcher isn't underneath, then we're unlocking to an app or custom launcher,
+ // neither of which have a smartspace.
+ if (!isNexusLauncherUnderneath()) {
+ return false
+ }
+
+ // TODO(b/213910911): Unfortunately the keyguard is hidden instantly on wake and unlock, so
+ // we won't have a lockscreen smartspace to animate. This is sad, and we should fix that!
+ if (biometricUnlockControllerLazy.get().isWakeAndUnlock) {
+ return false
+ }
+
+ // If we can't dismiss the lock screen via a swipe, then the only way we can do the shared
+ // element transition is if we're doing a biometric unlock. Otherwise, it means the bouncer
+ // is showing, and you can't see the lockscreen smartspace, so a shared element transition
+ // would not make sense.
+ if (!keyguardStateController.canDismissLockScreen() &&
+ !biometricUnlockControllerLazy.get().isBiometricUnlock) {
+ return false
+ }
+
+ unlockingWithSmartspaceTransition = true
+ smartspaceDestBounds.setEmpty()
+
+ // Assuming we were able to retrieve the launcher's state, start the lockscreen
+ // smartspace at 0, 0, and save its starting bounds.
+ with(lockscreenSmartspace!!) {
+ translationX = 0f
+ translationY = 0f
+ getBoundsOnScreen(smartspaceOriginBounds)
+ }
+
+ // Set the destination bounds to the launcher smartspace's bounds, offset by any
+ // padding on our smartspace.
+ with(smartspaceDestBounds) {
+ set(launcherSmartspaceState!!.boundsOnScreen)
+ offset(-lockscreenSmartspace!!.paddingLeft, -lockscreenSmartspace!!.paddingTop)
+ }
+
+ return true
+ }
+
+ /**
+ * Whether we should be able to do the in-window launcher animations given the current state of
+ * the device.
+ */
+ fun canPerformInWindowLauncherAnimations(): Boolean {
+ return isNexusLauncherUnderneath() && launcherUnlockController != null
+ }
+
+ /**
+ * Whether we are currently in the process of unlocking the keyguard, and we are performing the
+ * shared element SmartSpace transition.
+ */
+ fun isUnlockingWithSmartSpaceTransition(): Boolean {
+ return unlockingWithSmartspaceTransition
+ }
+
+ /**
+ * Whether this animation controller will be handling the unlock. We require remote animations
+ * to be enabled to do this.
+ *
+ * If this is not true, nothing in this class is relevant, and the unlock will be handled in
+ * [KeyguardViewMediator].
+ */
+ fun willHandleUnlockAnimation(): Boolean {
+ return KeyguardService.sEnableRemoteKeyguardGoingAwayAnimation
+ }
+
+ /**
+ * Whether we are playing a canned unlock animation, vs. unlocking from a touch gesture such as
+ * a swipe.
+ */
+ fun isPlayingCannedUnlockAnimation(): Boolean {
+ return playingCannedUnlockAnimation
+ }
+
+ companion object {
+ /**
+ * Return whether the Google Nexus launcher is underneath the keyguard, vs. some other
+ * launcher or an app. If so, we can communicate with it to perform in-window/shared element
+ * transitions!
+ */
+ fun isNexusLauncherUnderneath(): Boolean {
+ return ActivityManagerWrapper.getInstance()
+ .runningTask?.topActivity?.className?.equals(
+ QuickStepContract.LAUNCHER_ACTIVITY_CLASS_NAME) ?: false
+ }
+ }
} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 8376681e7bd4..08e1654c8dbf 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -2235,8 +2235,9 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
createInteractionJankMonitorConf("DismissPanel"));
// Pass the surface and metadata to the unlock animation controller.
- mKeyguardUnlockAnimationControllerLazy.get().notifyStartKeyguardExitAnimation(
- apps[0], startTime, mSurfaceBehindRemoteAnimationRequested);
+ mKeyguardUnlockAnimationControllerLazy.get()
+ .notifyStartSurfaceBehindRemoteAnimation(
+ apps[0], startTime, mSurfaceBehindRemoteAnimationRequested);
} else {
mInteractionJankMonitor.begin(
createInteractionJankMonitorConf("RemoteAnimationDisabled"));
@@ -2373,8 +2374,10 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
finishSurfaceBehindRemoteAnimation(cancelled);
mSurfaceBehindRemoteAnimationRequested = false;
- mKeyguardUnlockAnimationControllerLazy.get().notifyFinishedKeyguardExitAnimation();
});
+
+ mKeyguardUnlockAnimationControllerLazy.get().notifyFinishedKeyguardExitAnimation(
+ cancelled);
}
/**
@@ -2412,8 +2415,16 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
return mSurfaceBehindRemoteAnimationRequested;
}
+ public boolean isAnimatingBetweenKeyguardAndSurfaceBehind() {
+ return mSurfaceBehindRemoteAnimationRunning;
+ }
+
/** If it's running, finishes the RemoteAnimation on the surface behind the keyguard. */
public void finishSurfaceBehindRemoteAnimation(boolean cancelled) {
+ if (!mSurfaceBehindRemoteAnimationRunning) {
+ return;
+ }
+
mSurfaceBehindRemoteAnimationRunning = false;
if (mSurfaceBehindRemoteAnimationFinishedCallback != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
index d1fe7d449bdd..4baef3aef309 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
@@ -21,8 +21,6 @@ import android.content.Context;
import android.view.WindowManager;
import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.qualifiers.Background;
-import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.media.MediaDataManager;
import com.android.systemui.media.MediaHierarchyManager;
import com.android.systemui.media.MediaHost;
@@ -33,10 +31,8 @@ import com.android.systemui.media.taptotransfer.receiver.MediaTttChipControllerR
import com.android.systemui.media.taptotransfer.sender.MediaTttChipControllerSender;
import com.android.systemui.media.taptotransfer.sender.MediaTttSenderService;
import com.android.systemui.statusbar.commandline.CommandRegistry;
-import com.android.systemui.util.concurrency.DelayableExecutor;
import java.util.Optional;
-import java.util.concurrent.Executor;
import javax.inject.Named;
@@ -89,14 +85,11 @@ public interface MediaModule {
static Optional<MediaTttChipControllerSender> providesMediaTttChipControllerSender(
MediaTttFlags mediaTttFlags,
Context context,
- WindowManager windowManager,
- @Main Executor mainExecutor,
- @Background Executor backgroundExecutor) {
+ WindowManager windowManager) {
if (!mediaTttFlags.isMediaTttEnabled()) {
return Optional.empty();
}
- return Optional.of(new MediaTttChipControllerSender(
- context, windowManager, mainExecutor, backgroundExecutor));
+ return Optional.of(new MediaTttChipControllerSender(context, windowManager));
}
/** */
@@ -119,9 +112,7 @@ public interface MediaModule {
MediaTttFlags mediaTttFlags,
CommandRegistry commandRegistry,
Context context,
- MediaTttChipControllerSender mediaTttChipControllerSender,
- MediaTttChipControllerReceiver mediaTttChipControllerReceiver,
- @Main DelayableExecutor mainExecutor) {
+ MediaTttChipControllerReceiver mediaTttChipControllerReceiver) {
if (!mediaTttFlags.isMediaTttEnabled()) {
return Optional.empty();
}
@@ -129,9 +120,7 @@ public interface MediaModule {
new MediaTttCommandLineHelper(
commandRegistry,
context,
- mediaTttChipControllerSender,
- mediaTttChipControllerReceiver,
- mainExecutor));
+ mediaTttChipControllerReceiver));
}
/** Inject into MediaTttSenderService. */
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
index 460d38f45b4d..37208515120a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
@@ -28,21 +28,22 @@ import android.util.Log
import androidx.annotation.VisibleForTesting
import com.android.systemui.R
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.media.taptotransfer.receiver.MediaTttChipControllerReceiver
import com.android.systemui.media.taptotransfer.receiver.ChipStateReceiver
-import com.android.systemui.media.taptotransfer.sender.MediaTttChipControllerSender
import com.android.systemui.media.taptotransfer.sender.MediaTttSenderService
+import com.android.systemui.media.taptotransfer.sender.MoveCloserToEndCast
import com.android.systemui.media.taptotransfer.sender.MoveCloserToStartCast
-import com.android.systemui.media.taptotransfer.sender.TransferInitiated
-import com.android.systemui.media.taptotransfer.sender.TransferSucceeded
+import com.android.systemui.media.taptotransfer.sender.TransferFailed
+import com.android.systemui.media.taptotransfer.sender.TransferToReceiverTriggered
+import com.android.systemui.media.taptotransfer.sender.TransferToThisDeviceSucceeded
+import com.android.systemui.media.taptotransfer.sender.TransferToThisDeviceTriggered
+import com.android.systemui.media.taptotransfer.sender.TransferToReceiverSucceeded
import com.android.systemui.shared.mediattt.DeviceInfo
-import com.android.systemui.shared.mediattt.IDeviceSenderCallback
+import com.android.systemui.shared.mediattt.IDeviceSenderService
+import com.android.systemui.shared.mediattt.IUndoTransferCallback
import com.android.systemui.statusbar.commandline.Command
import com.android.systemui.statusbar.commandline.CommandRegistry
-import com.android.systemui.util.concurrency.DelayableExecutor
import java.io.PrintWriter
-import java.util.concurrent.FutureTask
import javax.inject.Inject
/**
@@ -53,11 +54,9 @@ import javax.inject.Inject
class MediaTttCommandLineHelper @Inject constructor(
commandRegistry: CommandRegistry,
private val context: Context,
- private val mediaTttChipControllerSender: MediaTttChipControllerSender,
private val mediaTttChipControllerReceiver: MediaTttChipControllerReceiver,
- @Main private val mainExecutor: DelayableExecutor,
) {
- private var senderCallback: IDeviceSenderCallback? = null
+ private var senderService: IDeviceSenderService? = null
private val senderServiceConnection = SenderServiceConnection()
private val appIconDrawable =
@@ -66,17 +65,15 @@ class MediaTttCommandLineHelper @Inject constructor(
}
init {
- commandRegistry.registerCommand(
- ADD_CHIP_COMMAND_SENDER_TAG) { AddChipCommandSender() }
- commandRegistry.registerCommand(
- REMOVE_CHIP_COMMAND_SENDER_TAG) { RemoveChipCommandSender() }
+ commandRegistry.registerCommand(SENDER_COMMAND) { SenderCommand() }
commandRegistry.registerCommand(
ADD_CHIP_COMMAND_RECEIVER_TAG) { AddChipCommandReceiver() }
commandRegistry.registerCommand(
REMOVE_CHIP_COMMAND_RECEIVER_TAG) { RemoveChipCommandReceiver() }
}
- inner class AddChipCommandSender : Command {
+ /** All commands for the sender device. */
+ inner class SenderCommand : Command {
override fun execute(pw: PrintWriter, args: List<String>) {
val otherDeviceName = args[0]
val mediaInfo = MediaRoute2Info.Builder("id", "Test Name")
@@ -86,62 +83,99 @@ class MediaTttCommandLineHelper @Inject constructor(
when (args[1]) {
MOVE_CLOSER_TO_START_CAST_COMMAND_NAME -> {
- runOnService { senderCallback ->
- senderCallback.closeToReceiverToStartCast(mediaInfo, otherDeviceInfo)
+ runOnService { senderService ->
+ senderService.closeToReceiverToStartCast(mediaInfo, otherDeviceInfo)
}
}
-
- // TODO(b/203800643): Migrate other commands to invoke the service instead of the
- // controller.
- TRANSFER_INITIATED_COMMAND_NAME -> {
- val futureTask = FutureTask { fakeUndoRunnable }
- mediaTttChipControllerSender.displayChip(
- TransferInitiated(
- appIconDrawable,
- APP_ICON_CONTENT_DESCRIPTION,
- otherDeviceName,
- futureTask
- )
- )
- mainExecutor.executeDelayed({ futureTask.run() }, FUTURE_WAIT_TIME)
-
+ MOVE_CLOSER_TO_END_CAST_COMMAND_NAME -> {
+ runOnService { senderService ->
+ senderService.closeToReceiverToEndCast(mediaInfo, otherDeviceInfo)
+ }
}
- TRANSFER_SUCCEEDED_COMMAND_NAME -> {
- mediaTttChipControllerSender.displayChip(
- TransferSucceeded(
- appIconDrawable,
- APP_ICON_CONTENT_DESCRIPTION,
- otherDeviceName,
- fakeUndoRunnable
- )
- )
+ TRANSFER_TO_RECEIVER_TRIGGERED_COMMAND_NAME -> {
+ runOnService { senderService ->
+ senderService.transferToReceiverTriggered(mediaInfo, otherDeviceInfo)
+ }
+ }
+ TRANSFER_TO_THIS_DEVICE_TRIGGERED_COMMAND_NAME -> {
+ runOnService { senderService ->
+ senderService.transferToThisDeviceTriggered(mediaInfo, otherDeviceInfo)
+ }
+ }
+ TRANSFER_TO_RECEIVER_SUCCEEDED_COMMAND_NAME -> {
+ val undoCallback = object : IUndoTransferCallback.Stub() {
+ override fun onUndoTriggered() {
+ Log.i(TAG, "Undo transfer to receiver callback triggered")
+ // The external services that implement this callback would kick off a
+ // transfer back to this device, so mimic that here.
+ runOnService { senderService ->
+ senderService
+ .transferToThisDeviceTriggered(mediaInfo, otherDeviceInfo)
+ }
+ }
+ }
+ runOnService { senderService ->
+ senderService
+ .transferToReceiverSucceeded(mediaInfo, otherDeviceInfo, undoCallback)
+ }
+ }
+ TRANSFER_TO_THIS_DEVICE_SUCCEEDED_COMMAND_NAME -> {
+ val undoCallback = object : IUndoTransferCallback.Stub() {
+ override fun onUndoTriggered() {
+ Log.i(TAG, "Undo transfer to this device callback triggered")
+ // The external services that implement this callback would kick off a
+ // transfer back to the receiver, so mimic that here.
+ runOnService { senderService ->
+ senderService
+ .transferToReceiverTriggered(mediaInfo, otherDeviceInfo)
+ }
+ }
+ }
+ runOnService { senderService ->
+ senderService
+ .transferToThisDeviceSucceeded(mediaInfo, otherDeviceInfo, undoCallback)
+ }
+ }
+ TRANSFER_FAILED_COMMAND_NAME -> {
+ runOnService { senderService ->
+ senderService.transferFailed(mediaInfo, otherDeviceInfo)
+ }
+ }
+ NO_LONGER_CLOSE_TO_RECEIVER_COMMAND_NAME -> {
+ runOnService { senderService ->
+ senderService.noLongerCloseToReceiver(mediaInfo, otherDeviceInfo)
+ context.unbindService(senderServiceConnection)
+ }
}
else -> {
- pw.println("Chip type must be one of " +
+ pw.println("Sender command must be one of " +
"$MOVE_CLOSER_TO_START_CAST_COMMAND_NAME, " +
- "$TRANSFER_INITIATED_COMMAND_NAME, " +
- TRANSFER_SUCCEEDED_COMMAND_NAME
+ "$MOVE_CLOSER_TO_END_CAST_COMMAND_NAME, " +
+ "$TRANSFER_TO_RECEIVER_TRIGGERED_COMMAND_NAME, " +
+ "$TRANSFER_TO_THIS_DEVICE_TRIGGERED_COMMAND_NAME, " +
+ "$TRANSFER_TO_RECEIVER_SUCCEEDED_COMMAND_NAME, " +
+ "$TRANSFER_TO_THIS_DEVICE_SUCCEEDED_COMMAND_NAME, " +
+ "$TRANSFER_FAILED_COMMAND_NAME, " +
+ NO_LONGER_CLOSE_TO_RECEIVER_COMMAND_NAME
)
}
}
}
override fun help(pw: PrintWriter) {
- pw.println("Usage: adb shell cmd statusbar " +
- "$ADD_CHIP_COMMAND_SENDER_TAG <deviceName> <chipStatus>"
- )
+ pw.println("Usage: adb shell cmd statusbar $SENDER_COMMAND <deviceName> <chipStatus>")
}
- private fun runOnService(command: SenderCallbackCommand) {
- val currentServiceCallback = senderCallback
- if (currentServiceCallback != null) {
- command.run(currentServiceCallback)
+ private fun runOnService(command: SenderServiceCommand) {
+ val currentService = senderService
+ if (currentService != null) {
+ command.run(currentService)
} else {
bindService(command)
}
}
- private fun bindService(command: SenderCallbackCommand) {
+ private fun bindService(command: SenderServiceCommand) {
senderServiceConnection.pendingCommand = command
val binding = context.bindService(
Intent(context, MediaTttSenderService::class.java),
@@ -152,19 +186,6 @@ class MediaTttCommandLineHelper @Inject constructor(
}
}
- /** A command to REMOVE the media ttt chip on the SENDER device. */
- inner class RemoveChipCommandSender : Command {
- override fun execute(pw: PrintWriter, args: List<String>) {
- mediaTttChipControllerSender.removeChip()
- if (senderCallback != null) {
- context.unbindService(senderServiceConnection)
- }
- }
- override fun help(pw: PrintWriter) {
- pw.println("Usage: adb shell cmd statusbar $REMOVE_CHIP_COMMAND_SENDER_TAG")
- }
- }
-
/** A command to DISPLAY the media ttt chip on the RECEIVER device. */
inner class AddChipCommandReceiver : Command {
override fun execute(pw: PrintWriter, args: List<String>) {
@@ -187,36 +208,32 @@ class MediaTttCommandLineHelper @Inject constructor(
}
}
- /** A service connection for [IDeviceSenderCallback]. */
+ /** A service connection for [IDeviceSenderService]. */
private inner class SenderServiceConnection : ServiceConnection {
// A command that should be run when the service gets connected.
- var pendingCommand: SenderCallbackCommand? = null
+ var pendingCommand: SenderServiceCommand? = null
override fun onServiceConnected(className: ComponentName, service: IBinder) {
- val newCallback = IDeviceSenderCallback.Stub.asInterface(service)
- senderCallback = newCallback
+ val newCallback = IDeviceSenderService.Stub.asInterface(service)
+ senderService = newCallback
pendingCommand?.run(newCallback)
pendingCommand = null
}
override fun onServiceDisconnected(className: ComponentName) {
- senderCallback = null
+ senderService = null
}
}
- /** An interface defining a command that should be run on the sender callback. */
- private fun interface SenderCallbackCommand {
- /** Runs the command on the provided [senderCallback]. */
- fun run(senderCallback: IDeviceSenderCallback)
- }
-
- private val fakeUndoRunnable = Runnable {
- Log.i(TAG, "Undo runnable triggered")
+ /** An interface defining a command that should be run on the sender service. */
+ private fun interface SenderServiceCommand {
+ /** Runs the command on the provided [senderService]. */
+ fun run(senderService: IDeviceSenderService)
}
}
@VisibleForTesting
-const val ADD_CHIP_COMMAND_SENDER_TAG = "media-ttt-chip-add-sender"
+const val SENDER_COMMAND = "media-ttt-chip-sender"
@VisibleForTesting
const val REMOVE_CHIP_COMMAND_SENDER_TAG = "media-ttt-chip-remove-sender"
@VisibleForTesting
@@ -226,10 +243,21 @@ const val REMOVE_CHIP_COMMAND_RECEIVER_TAG = "media-ttt-chip-remove-receiver"
@VisibleForTesting
val MOVE_CLOSER_TO_START_CAST_COMMAND_NAME = MoveCloserToStartCast::class.simpleName!!
@VisibleForTesting
-val TRANSFER_INITIATED_COMMAND_NAME = TransferInitiated::class.simpleName!!
+val MOVE_CLOSER_TO_END_CAST_COMMAND_NAME = MoveCloserToEndCast::class.simpleName!!
+@VisibleForTesting
+val TRANSFER_TO_RECEIVER_TRIGGERED_COMMAND_NAME = TransferToReceiverTriggered::class.simpleName!!
+@VisibleForTesting
+val TRANSFER_TO_THIS_DEVICE_TRIGGERED_COMMAND_NAME =
+ TransferToThisDeviceTriggered::class.simpleName!!
+@VisibleForTesting
+val TRANSFER_TO_RECEIVER_SUCCEEDED_COMMAND_NAME = TransferToReceiverSucceeded::class.simpleName!!
+@VisibleForTesting
+val TRANSFER_TO_THIS_DEVICE_SUCCEEDED_COMMAND_NAME =
+ TransferToThisDeviceSucceeded::class.simpleName!!
+@VisibleForTesting
+val TRANSFER_FAILED_COMMAND_NAME = TransferFailed::class.simpleName!!
@VisibleForTesting
-val TRANSFER_SUCCEEDED_COMMAND_NAME = TransferSucceeded::class.simpleName!!
+val NO_LONGER_CLOSE_TO_RECEIVER_COMMAND_NAME = "NoLongerCloseToReceiver"
-private const val FUTURE_WAIT_TIME = 2000L
private const val APP_ICON_CONTENT_DESCRIPTION = "Fake media app icon"
private const val TAG = "MediaTapToTransferCli"
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt
index 67721a543427..adae07b58e7c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt
@@ -81,6 +81,9 @@ abstract class MediaTttChipControllerCommon<T : MediaTttChipState>(
/** Hides the chip. */
fun removeChip() {
+ // TODO(b/203800347): We may not want to hide the chip if we're currently in a
+ // TransferTriggered state: Once the user has initiated the transfer, they should be able
+ // to move away from the receiver device but still see the status of the transfer.
if (chipView == null) { return }
windowManager.removeView(chipView)
chipView = null
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
index dd434e7756fb..c656df2e0a35 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
@@ -16,11 +16,12 @@
package com.android.systemui.media.taptotransfer.sender
+import android.content.Context
import android.graphics.drawable.Drawable
-import androidx.annotation.StringRes
+import android.view.View
import com.android.systemui.R
import com.android.systemui.media.taptotransfer.common.MediaTttChipState
-import java.util.concurrent.Future
+import com.android.systemui.shared.mediattt.IUndoTransferCallback
/**
* A class that stores all the information necessary to display the media tap-to-transfer chip on
@@ -28,66 +29,181 @@ import java.util.concurrent.Future
*
* This is a sealed class where each subclass represents a specific chip state. Each subclass can
* contain additional information that is necessary for only that state.
- *
- * @property chipText a string resource for the text that the chip should display.
- * @property otherDeviceName the name of the other device involved in the transfer.
*/
sealed class ChipStateSender(
appIconDrawable: Drawable,
- appIconContentDescription: String,
- @StringRes internal val chipText: Int,
- internal val otherDeviceName: String,
-) : MediaTttChipState(appIconDrawable, appIconContentDescription)
+ appIconContentDescription: String
+) : MediaTttChipState(appIconDrawable, appIconContentDescription) {
+ /** Returns a fully-formed string with the text that the chip should display. */
+ abstract fun getChipTextString(context: Context): String
+
+ /** Returns true if the loading icon should be displayed and false otherwise. */
+ open fun showLoading(): Boolean = false
+
+ /**
+ * Returns a click listener for the undo button on the chip. Returns null if this chip state
+ * doesn't have an undo button.
+ *
+ * @param controllerSender passed as a parameter in case we want to display a new chip state
+ * when undo is clicked.
+ */
+ open fun undoClickListener(
+ controllerSender: MediaTttChipControllerSender
+ ): View.OnClickListener? = null
+}
/**
* A state representing that the two devices are close but not close enough to *start* a cast to
* the receiver device. The chip will instruct the user to move closer in order to initiate the
* transfer to the receiver.
+ *
+ * @property otherDeviceName the name of the other device involved in the transfer.
*/
class MoveCloserToStartCast(
appIconDrawable: Drawable,
appIconContentDescription: String,
- otherDeviceName: String,
-) : ChipStateSender(
- appIconDrawable,
- appIconContentDescription,
- R.string.media_move_closer_to_start_cast,
- otherDeviceName
-)
+ private val otherDeviceName: String,
+) : ChipStateSender(appIconDrawable, appIconContentDescription) {
+ override fun getChipTextString(context: Context): String {
+ return context.getString(R.string.media_move_closer_to_start_cast, otherDeviceName)
+ }
+}
/**
- * A state representing that a transfer has been initiated (but not completed).
+ * A state representing that the two devices are close but not close enough to *end* a cast that's
+ * currently occurring the receiver device. The chip will instruct the user to move closer in order
+ * to initiate the transfer from the receiver and back onto this device (the original sender).
*
- * @property future a future that will be resolved when the transfer has either succeeded or failed.
- * If the transfer succeeded, the future can optionally return an undo runnable (see
- * [TransferSucceeded.undoRunnable]). [MediaTttChipControllerSender] is responsible for transitioning
- * the chip to the [TransferSucceeded] state if the future resolves successfully.
+ * @property otherDeviceName the name of the other device involved in the transfer.
*/
-class TransferInitiated(
+class MoveCloserToEndCast(
appIconDrawable: Drawable,
appIconContentDescription: String,
- otherDeviceName: String,
- val future: Future<Runnable?>
-) : ChipStateSender(
- appIconDrawable,
- appIconContentDescription,
- R.string.media_transfer_playing,
- otherDeviceName
-)
+ private val otherDeviceName: String,
+) : ChipStateSender(appIconDrawable, appIconContentDescription) {
+ override fun getChipTextString(context: Context): String {
+ return context.getString(R.string.media_move_closer_to_end_cast, otherDeviceName)
+ }
+}
/**
- * A state representing that a transfer has been successfully completed.
+ * A state representing that a transfer to the receiver device has been initiated (but not
+ * completed).
*
- * @property undoRunnable if present, the runnable that should be run to undo the transfer. We will
- * show an Undo button on the chip if this runnable is present.
+ * @property otherDeviceName the name of the other device involved in the transfer.
*/
-class TransferSucceeded(
+class TransferToReceiverTriggered(
appIconDrawable: Drawable,
appIconContentDescription: String,
- otherDeviceName: String,
- val undoRunnable: Runnable? = null
-) : ChipStateSender(appIconDrawable,
- appIconContentDescription,
- R.string.media_transfer_playing,
- otherDeviceName
-)
+ private val otherDeviceName: String
+) : ChipStateSender(appIconDrawable, appIconContentDescription) {
+ override fun getChipTextString(context: Context): String {
+ return context.getString(R.string.media_transfer_playing_different_device, otherDeviceName)
+ }
+
+ override fun showLoading() = true
+}
+
+/**
+ * A state representing that a transfer from the receiver device and back to this device (the
+ * sender) has been initiated (but not completed).
+ */
+class TransferToThisDeviceTriggered(
+ appIconDrawable: Drawable,
+ appIconContentDescription: String
+) : ChipStateSender(appIconDrawable, appIconContentDescription) {
+ override fun getChipTextString(context: Context): String {
+ return context.getString(R.string.media_transfer_playing_this_device)
+ }
+
+ override fun showLoading() = true
+}
+
+/**
+ * A state representing that a transfer to the receiver device has been successfully completed.
+ *
+ * @property otherDeviceName the name of the other device involved in the transfer.
+ * @property undoCallback if present, the callback that should be called when the user clicks the
+ * undo button. The undo button will only be shown if this is non-null.
+ */
+class TransferToReceiverSucceeded(
+ appIconDrawable: Drawable,
+ appIconContentDescription: String,
+ private val otherDeviceName: String,
+ val undoCallback: IUndoTransferCallback? = null
+) : ChipStateSender(appIconDrawable, appIconContentDescription) {
+ override fun getChipTextString(context: Context): String {
+ return context.getString(R.string.media_transfer_playing_different_device, otherDeviceName)
+ }
+
+ override fun undoClickListener(
+ controllerSender: MediaTttChipControllerSender
+ ): View.OnClickListener? {
+ if (undoCallback == null) {
+ return null
+ }
+
+ return View.OnClickListener {
+ this.undoCallback.onUndoTriggered()
+ // The external service should eventually send us a TransferToThisDeviceTriggered state,
+ // but that may take too long to go through the binder and the user may be confused as
+ // to why the UI hasn't changed yet. So, we immediately change the UI here.
+ controllerSender.displayChip(
+ TransferToThisDeviceTriggered(
+ this.appIconDrawable,
+ this.appIconContentDescription
+ )
+ )
+ }
+ }
+}
+
+/**
+ * A state representing that a transfer back to this device has been successfully completed.
+ *
+ * @property otherDeviceName the name of the other device involved in the transfer.
+ * @property undoCallback if present, the callback that should be called when the user clicks the
+ * undo button. The undo button will only be shown if this is non-null.
+ */
+class TransferToThisDeviceSucceeded(
+ appIconDrawable: Drawable,
+ appIconContentDescription: String,
+ private val otherDeviceName: String,
+ val undoCallback: IUndoTransferCallback? = null
+) : ChipStateSender(appIconDrawable, appIconContentDescription) {
+ override fun getChipTextString(context: Context): String {
+ return context.getString(R.string.media_transfer_playing_this_device)
+ }
+
+ override fun undoClickListener(
+ controllerSender: MediaTttChipControllerSender
+ ): View.OnClickListener? {
+ if (undoCallback == null) {
+ return null
+ }
+
+ return View.OnClickListener {
+ this.undoCallback.onUndoTriggered()
+ // The external service should eventually send us a TransferToReceiverTriggered state,
+ // but that may take too long to go through the binder and the user may be confused as
+ // to why the UI hasn't changed yet. So, we immediately change the UI here.
+ controllerSender.displayChip(
+ TransferToReceiverTriggered(
+ this.appIconDrawable,
+ this.appIconContentDescription,
+ this.otherDeviceName
+ )
+ )
+ }
+ }
+}
+
+/** A state representing that a transfer has failed. */
+class TransferFailed(
+ appIconDrawable: Drawable,
+ appIconContentDescription: String
+) : ChipStateSender(appIconDrawable, appIconContentDescription) {
+ override fun getChipTextString(context: Context): String {
+ return context.getString(R.string.media_transfer_failed)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
index 77d3d70fc98c..453e3d627bc8 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
@@ -23,11 +23,7 @@ import android.view.WindowManager
import android.widget.TextView
import com.android.systemui.R
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.media.taptotransfer.common.MediaTttChipControllerCommon
-import java.util.concurrent.Executor
-import java.util.concurrent.TimeUnit
import javax.inject.Inject
/**
@@ -38,8 +34,6 @@ import javax.inject.Inject
class MediaTttChipControllerSender @Inject constructor(
context: Context,
windowManager: WindowManager,
- @Main private val mainExecutor: Executor,
- @Background private val backgroundExecutor: Executor,
) : MediaTttChipControllerCommon<ChipStateSender>(
context, windowManager, R.layout.media_ttt_chip
) {
@@ -51,63 +45,22 @@ class MediaTttChipControllerSender @Inject constructor(
// Text
currentChipView.requireViewById<TextView>(R.id.text).apply {
- text = context.getString(chipState.chipText, chipState.otherDeviceName)
+ text = chipState.getChipTextString(context)
}
// Loading
- val showLoading = chipState is TransferInitiated
currentChipView.requireViewById<View>(R.id.loading).visibility =
- if (showLoading) { View.VISIBLE } else { View.GONE }
+ if (chipState.showLoading()) { View.VISIBLE } else { View.GONE }
// Undo
- val undoClickListener: View.OnClickListener? =
- if (chipState is TransferSucceeded && chipState.undoRunnable != null)
- View.OnClickListener { chipState.undoRunnable.run() }
- else
- null
val undoView = currentChipView.requireViewById<View>(R.id.undo)
- undoView.visibility = if (undoClickListener != null) {
- View.VISIBLE
- } else {
- View.GONE
- }
+ val undoClickListener = chipState.undoClickListener(this)
undoView.setOnClickListener(undoClickListener)
+ undoView.visibility = if (undoClickListener != null) { View.VISIBLE } else { View.GONE }
- // Future handling
- if (chipState is TransferInitiated) {
- addFutureCallback(chipState)
- }
- }
-
- /**
- * Adds the appropriate callbacks to [chipState.future] so that we update the chip correctly
- * when the future resolves.
- */
- private fun addFutureCallback(chipState: TransferInitiated) {
- // Listen to the future on a background thread so we don't occupy the main thread while we
- // wait for it to complete.
- backgroundExecutor.execute {
- try {
- val undoRunnable = chipState.future.get(TRANSFER_TIMEOUT_SECONDS, TimeUnit.SECONDS)
- // Make UI changes on the main thread
- mainExecutor.execute {
- displayChip(
- TransferSucceeded(
- chipState.appIconDrawable,
- chipState.appIconContentDescription,
- chipState.otherDeviceName,
- undoRunnable
- )
- )
- }
- } catch (ex: Exception) {
- // TODO(b/203800327): Maybe show a failure chip here if UX decides we need one.
- mainExecutor.execute {
- removeChip()
- }
- }
- }
+ // Failure
+ val showFailure = chipState is TransferFailed
+ currentChipView.requireViewById<View>(R.id.failure_icon).visibility =
+ if (showFailure) { View.VISIBLE } else { View.GONE }
}
}
-
-private const val TRANSFER_TIMEOUT_SECONDS = 10L
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderService.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderService.kt
index b56a69903ea4..717752e536b0 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderService.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderService.kt
@@ -25,7 +25,8 @@ import android.media.MediaRoute2Info
import android.os.IBinder
import com.android.systemui.R
import com.android.systemui.shared.mediattt.DeviceInfo
-import com.android.systemui.shared.mediattt.IDeviceSenderCallback
+import com.android.systemui.shared.mediattt.IUndoTransferCallback
+import com.android.systemui.shared.mediattt.IDeviceSenderService
import javax.inject.Inject
/**
@@ -37,12 +38,63 @@ class MediaTttSenderService @Inject constructor(
) : Service() {
// TODO(b/203800643): Add logging when callbacks trigger.
- private val binder: IBinder = object : IDeviceSenderCallback.Stub() {
+ private val binder: IBinder = object : IDeviceSenderService.Stub() {
override fun closeToReceiverToStartCast(
mediaInfo: MediaRoute2Info, otherDeviceInfo: DeviceInfo
) {
this@MediaTttSenderService.closeToReceiverToStartCast(mediaInfo, otherDeviceInfo)
}
+
+ override fun closeToReceiverToEndCast(
+ mediaInfo: MediaRoute2Info, otherDeviceInfo: DeviceInfo
+ ) {
+ this@MediaTttSenderService.closeToReceiverToEndCast(mediaInfo, otherDeviceInfo)
+ }
+
+ override fun transferFailed(
+ mediaInfo: MediaRoute2Info, otherDeviceInfo: DeviceInfo
+ ) {
+ this@MediaTttSenderService.transferFailed(mediaInfo)
+ }
+
+ override fun transferToReceiverTriggered(
+ mediaInfo: MediaRoute2Info, otherDeviceInfo: DeviceInfo
+ ) {
+ this@MediaTttSenderService.transferToReceiverTriggered(mediaInfo, otherDeviceInfo)
+ }
+
+ override fun transferToThisDeviceTriggered(
+ mediaInfo: MediaRoute2Info, otherDeviceInfo: DeviceInfo
+ ) {
+ this@MediaTttSenderService.transferToThisDeviceTriggered(mediaInfo)
+ }
+
+ override fun transferToReceiverSucceeded(
+ mediaInfo: MediaRoute2Info,
+ otherDeviceInfo: DeviceInfo,
+ undoCallback: IUndoTransferCallback
+ ) {
+ this@MediaTttSenderService.transferToReceiverSucceeded(
+ mediaInfo, otherDeviceInfo, undoCallback
+ )
+ }
+
+ override fun transferToThisDeviceSucceeded(
+ mediaInfo: MediaRoute2Info,
+ otherDeviceInfo: DeviceInfo,
+ undoCallback: IUndoTransferCallback
+ ) {
+ this@MediaTttSenderService.transferToThisDeviceSucceeded(
+ mediaInfo, otherDeviceInfo, undoCallback
+ )
+ }
+
+ override fun noLongerCloseToReceiver(
+ mediaInfo: MediaRoute2Info,
+ otherDeviceInfo: DeviceInfo
+ ) {
+ this@MediaTttSenderService.noLongerCloseToReceiver()
+ }
}
// TODO(b/203800643): Use the app icon from the media info instead of a fake one.
@@ -63,4 +115,68 @@ class MediaTttSenderService @Inject constructor(
)
controller.displayChip(chipState)
}
+
+ private fun closeToReceiverToEndCast(mediaInfo: MediaRoute2Info, otherDeviceInfo: DeviceInfo) {
+ val chipState = MoveCloserToEndCast(
+ appIconDrawable = fakeAppIconDrawable,
+ appIconContentDescription = mediaInfo.name.toString(),
+ otherDeviceName = otherDeviceInfo.name
+ )
+ controller.displayChip(chipState)
+ }
+
+ private fun transferFailed(mediaInfo: MediaRoute2Info) {
+ val chipState = TransferFailed(
+ appIconDrawable = fakeAppIconDrawable,
+ appIconContentDescription = mediaInfo.name.toString()
+ )
+ controller.displayChip(chipState)
+ }
+
+ private fun transferToReceiverTriggered(
+ mediaInfo: MediaRoute2Info, otherDeviceInfo: DeviceInfo
+ ) {
+ val chipState = TransferToReceiverTriggered(
+ appIconDrawable = fakeAppIconDrawable,
+ appIconContentDescription = mediaInfo.name.toString(),
+ otherDeviceName = otherDeviceInfo.name
+ )
+ controller.displayChip(chipState)
+ }
+
+ private fun transferToThisDeviceTriggered(mediaInfo: MediaRoute2Info) {
+ val chipState = TransferToThisDeviceTriggered(
+ appIconDrawable = fakeAppIconDrawable,
+ appIconContentDescription = mediaInfo.name.toString()
+ )
+ controller.displayChip(chipState)
+ }
+
+ private fun transferToReceiverSucceeded(
+ mediaInfo: MediaRoute2Info, otherDeviceInfo: DeviceInfo, undoCallback: IUndoTransferCallback
+ ) {
+ val chipState = TransferToReceiverSucceeded(
+ appIconDrawable = fakeAppIconDrawable,
+ appIconContentDescription = mediaInfo.name.toString(),
+ otherDeviceName = otherDeviceInfo.name,
+ undoCallback = undoCallback
+ )
+ controller.displayChip(chipState)
+ }
+
+ private fun transferToThisDeviceSucceeded(
+ mediaInfo: MediaRoute2Info, otherDeviceInfo: DeviceInfo, undoCallback: IUndoTransferCallback
+ ) {
+ val chipState = TransferToThisDeviceSucceeded(
+ appIconDrawable = fakeAppIconDrawable,
+ appIconContentDescription = mediaInfo.name.toString(),
+ otherDeviceName = otherDeviceInfo.name,
+ undoCallback = undoCallback
+ )
+ controller.displayChip(chipState)
+ }
+
+ private fun noLongerCloseToReceiver() {
+ controller.removeChip()
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index d190dcb3ffb8..1bef32ad8caf 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -141,6 +141,7 @@ import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.wm.shell.back.BackAnimation;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
import com.android.wm.shell.pip.Pip;
@@ -190,6 +191,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
private final Optional<Pip> mPipOptional;
private final Optional<LegacySplitScreen> mSplitScreenOptional;
private final Optional<Recents> mRecentsOptional;
+ private final Optional<BackAnimation> mBackAnimation;
private final SystemActions mSystemActions;
private final Handler mHandler;
private final NavigationBarOverlayController mNavbarOverlayController;
@@ -504,7 +506,8 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
AutoHideController mainAutoHideController,
AutoHideController.Factory autoHideControllerFactory,
Optional<TelecomManager> telecomManagerOptional,
- InputMethodManager inputMethodManager) {
+ InputMethodManager inputMethodManager,
+ Optional<BackAnimation> backAnimation) {
mContext = context;
mWindowManager = windowManager;
mAccessibilityManager = accessibilityManager;
@@ -524,6 +527,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
mPipOptional = pipOptional;
mSplitScreenOptional = splitScreenOptional;
mRecentsOptional = recentsOptional;
+ mBackAnimation = backAnimation;
mSystemActions = systemActions;
mHandler = mainHandler;
mNavbarOverlayController = navbarOverlayController;
@@ -629,6 +633,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
mSplitScreenOptional.ifPresent(mNavigationBarView::registerDockedListener);
mPipOptional.ifPresent(mNavigationBarView::addPipExclusionBoundsChangeListener);
+ mBackAnimation.ifPresent(mNavigationBarView::registerBackAnimation);
prepareNavigationBarView();
checkNavBarModes();
@@ -1680,6 +1685,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
private final AutoHideController.Factory mAutoHideControllerFactory;
private final Optional<TelecomManager> mTelecomManagerOptional;
private final InputMethodManager mInputMethodManager;
+ private final Optional<BackAnimation> mBackAnimation;
@Inject
public Factory(
@@ -1712,7 +1718,8 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
AutoHideController mainAutoHideController,
AutoHideController.Factory autoHideControllerFactory,
Optional<TelecomManager> telecomManagerOptional,
- InputMethodManager inputMethodManager) {
+ InputMethodManager inputMethodManager,
+ Optional<BackAnimation> backAnimation) {
mAssistManagerLazy = assistManagerLazy;
mAccessibilityManager = accessibilityManager;
mDeviceProvisionedController = deviceProvisionedController;
@@ -1743,6 +1750,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
mAutoHideControllerFactory = autoHideControllerFactory;
mTelecomManagerOptional = telecomManagerOptional;
mInputMethodManager = inputMethodManager;
+ mBackAnimation = backAnimation;
}
/** Construct a {@link NavigationBar} */
@@ -1759,7 +1767,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
mNavbarOverlayController, mUiEventLogger, mNavBarHelper,
mUserTracker, mMainLightBarController, mLightBarControllerFactory,
mMainAutoHideController, mAutoHideControllerFactory, mTelecomManagerOptional,
- mInputMethodManager);
+ mInputMethodManager, mBackAnimation);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
index a984974c6bba..98b49b1c4890 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -59,6 +59,7 @@ import com.android.systemui.statusbar.phone.AutoHideController;
import com.android.systemui.statusbar.phone.BarTransitions.TransitionMode;
import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.wm.shell.back.BackAnimation;
import com.android.wm.shell.pip.Pip;
import java.io.FileDescriptor;
@@ -109,7 +110,8 @@ public class NavigationBarController implements
DumpManager dumpManager,
AutoHideController autoHideController,
LightBarController lightBarController,
- Optional<Pip> pipOptional) {
+ Optional<Pip> pipOptional,
+ Optional<BackAnimation> backAnimation) {
mContext = context;
mHandler = mainHandler;
mNavigationBarFactory = navigationBarFactory;
@@ -121,7 +123,8 @@ public class NavigationBarController implements
mTaskbarDelegate = taskbarDelegate;
mTaskbarDelegate.setDependencies(commandQueue, overviewProxyService,
navBarHelper, navigationModeController, sysUiFlagsContainer,
- dumpManager, autoHideController, lightBarController, pipOptional);
+ dumpManager, autoHideController, lightBarController, pipOptional,
+ backAnimation.orElse(null));
mIsTablet = isTablet(mContext);
dumpManager.registerDumpable(this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
index ac816ba9e8d5..2dd89f3c4dcd 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
@@ -91,6 +91,7 @@ import com.android.systemui.statusbar.phone.AutoHideController;
import com.android.systemui.statusbar.phone.LightBarTransitionsController;
import com.android.systemui.statusbar.phone.NotificationPanelViewController;
import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.wm.shell.back.BackAnimation;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
import com.android.wm.shell.pip.Pip;
@@ -1417,6 +1418,10 @@ public class NavigationBarView extends FrameLayout implements
pip.removePipExclusionBoundsChangeListener(mPipListener);
}
+ void registerBackAnimation(BackAnimation backAnimation) {
+ mEdgeBackGestureHandler.setBackAnimation(backAnimation);
+ }
+
private static void dumpButton(PrintWriter pw, String caption, ButtonDispatcher button) {
pw.print(" " + caption + ": ");
if (button == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
index 002dd10f7356..441e79a97521 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
@@ -69,6 +69,7 @@ import com.android.systemui.statusbar.phone.AutoHideController;
import com.android.systemui.statusbar.phone.BarTransitions;
import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.statusbar.phone.LightBarTransitionsController;
+import com.android.wm.shell.back.BackAnimation;
import com.android.wm.shell.pip.Pip;
import java.io.FileDescriptor;
@@ -150,6 +151,7 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
mAutoHideController.touchAutoHide();
}
};
+ private BackAnimation mBackAnimation;
@Inject
public TaskbarDelegate(Context context) {
@@ -172,7 +174,8 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
SysUiState sysUiState, DumpManager dumpManager,
AutoHideController autoHideController,
LightBarController lightBarController,
- Optional<Pip> pipOptional) {
+ Optional<Pip> pipOptional,
+ BackAnimation backAnimation) {
// TODO: adding this in the ctor results in a dagger dependency cycle :(
mCommandQueue = commandQueue;
mOverviewProxyService = overviewProxyService;
@@ -184,6 +187,7 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
mLightBarController = lightBarController;
mLightBarTransitionsController = createLightBarTransitionsController();
mPipOptional = pipOptional;
+ mBackAnimation = backAnimation;
}
// Separated into a method to keep setDependencies() clean/readable.
@@ -233,6 +237,7 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
mAutoHideController.setNavigationBar(mAutoHideUiElement);
mLightBarController.setNavigationBar(mLightBarTransitionsController);
mPipOptional.ifPresent(this::addPipExclusionBoundsChangeListener);
+ mEdgeBackGestureHandler.setBackAnimation(mBackAnimation);
mInitialized = true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index ab48a28facd0..4f4bd1e86e7c 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -36,6 +36,7 @@ import android.os.Looper;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.Trace;
import android.provider.DeviceConfig;
import android.util.DisplayMetrics;
import android.util.Log;
@@ -79,6 +80,7 @@ import com.android.systemui.shared.tracing.ProtoTraceable;
import com.android.systemui.tracing.ProtoTracer;
import com.android.systemui.tracing.nano.EdgeBackGestureHandlerProto;
import com.android.systemui.tracing.nano.SystemUiTraceProto;
+import com.android.wm.shell.back.BackAnimation;
import java.io.PrintWriter;
import java.util.ArrayDeque;
@@ -231,6 +233,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
private InputChannelCompat.InputEventReceiver mInputEventReceiver;
private NavigationEdgeBackPlugin mEdgeBackPlugin;
+ private BackAnimation mBackAnimation;
private int mLeftInset;
private int mRightInset;
private int mSysUiFlags;
@@ -494,7 +497,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
Choreographer.getInstance(), this::onInputEvent);
// Add a nav bar panel window
- setEdgeBackPlugin(new NavigationBarEdgePanel(mContext));
+ setEdgeBackPlugin(new NavigationBarEdgePanel(mContext, mBackAnimation));
mPluginManager.addPluginListener(
this, NavigationEdgeBackPlugin.class, /*allowMultiple=*/ false);
}
@@ -509,7 +512,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
@Override
public void onPluginDisconnected(NavigationEdgeBackPlugin plugin) {
- setEdgeBackPlugin(new NavigationBarEdgePanel(mContext));
+ setEdgeBackPlugin(new NavigationBarEdgePanel(mContext, mBackAnimation));
}
private void setEdgeBackPlugin(NavigationEdgeBackPlugin edgeBackPlugin) {
@@ -576,7 +579,9 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
mMLModelThreshold = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_SYSTEMUI,
SystemUiDeviceConfigFlags.BACK_GESTURE_ML_MODEL_THRESHOLD, 0.9f);
if (mBackGestureTfClassifierProvider.isActive()) {
+ Trace.beginSection("EdgeBackGestureHandler#loadVocab");
mVocab = mBackGestureTfClassifierProvider.loadVocab(mContext.getAssets());
+ Trace.endSection();
mUseMLModel = true;
return;
}
@@ -930,6 +935,10 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
proto.edgeBackGestureHandler.allowGesture = mAllowGesture;
}
+ public void setBackAnimation(BackAnimation backAnimation) {
+ mBackAnimation = backAnimation;
+ }
+
/**
* Injectable instance to create a new EdgeBackGestureHandler.
*
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java
index 8d1dfc842fba..c18209d9abca 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java
@@ -57,6 +57,7 @@ import com.android.systemui.animation.Interpolators;
import com.android.systemui.plugins.NavigationEdgeBackPlugin;
import com.android.systemui.shared.navigationbar.RegionSamplingHelper;
import com.android.systemui.statusbar.VibratorHelper;
+import com.android.wm.shell.back.BackAnimation;
import java.io.PrintWriter;
import java.util.concurrent.Executor;
@@ -277,11 +278,14 @@ public class NavigationBarEdgePanel extends View implements NavigationEdgeBackPl
}
};
private BackCallback mBackCallback;
+ private final BackAnimation mBackAnimation;
- public NavigationBarEdgePanel(Context context) {
+ public NavigationBarEdgePanel(Context context,
+ BackAnimation backAnimation) {
super(context);
mWindowManager = context.getSystemService(WindowManager.class);
+ mBackAnimation = backAnimation;
mVibratorHelper = Dependency.get(VibratorHelper.class);
mDensity = context.getResources().getDisplayMetrics().density;
@@ -459,6 +463,9 @@ public class NavigationBarEdgePanel extends View implements NavigationEdgeBackPl
@Override
public void onMotionEvent(MotionEvent event) {
+ if (mBackAnimation != null) {
+ mBackAnimation.onBackMotion(event);
+ }
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
@@ -866,6 +873,9 @@ public class NavigationBarEdgePanel extends View implements NavigationEdgeBackPl
// Whenever the trigger back state changes the existing translation animation should be
// cancelled
mTranslationAnimation.cancel();
+ if (mBackAnimation != null) {
+ mBackAnimation.setTriggerBack(triggerBack);
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerController.java b/packages/SystemUI/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerController.java
index c8e2ca7e7ea8..e26c768a5e80 100644
--- a/packages/SystemUI/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerController.java
+++ b/packages/SystemUI/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerController.java
@@ -93,6 +93,7 @@ public class QRCodeScannerController implements
private final DeviceConfigProxy mDeviceConfigProxy;
private final ArrayList<Callback> mCallbacks = new ArrayList<>();
private final UserTracker mUserTracker;
+ private final boolean mConfigEnableLockScreenButton;
private HashMap<Integer, ContentObserver> mQRCodeScannerPreferenceObserver = new HashMap<>();
private DeviceConfig.OnPropertiesChangedListener mOnDefaultQRCodeScannerChangedListener = null;
@@ -118,6 +119,9 @@ public class QRCodeScannerController implements
mSecureSettings = secureSettings;
mDeviceConfigProxy = proxy;
mUserTracker = userTracker;
+
+ mConfigEnableLockScreenButton = mContext.getResources().getBoolean(
+ android.R.bool.config_enableQrCodeScannerOnLockScreen);
}
/**
@@ -156,7 +160,7 @@ public class QRCodeScannerController implements
* Returns true if lock screen entry point for QR Code Scanner is to be enabled.
*/
public boolean isEnabledForLockScreenButton() {
- return mQRCodeScannerEnabled && mIntent != null;
+ return mQRCodeScannerEnabled && mIntent != null && mConfigEnableLockScreenButton;
}
/**
@@ -235,6 +239,11 @@ public class QRCodeScannerController implements
}
private void updateQRCodeScannerPreferenceDetails(boolean updateSettings) {
+ if (!mConfigEnableLockScreenButton) {
+ // Settings only apply to lock screen entry point.
+ return;
+ }
+
boolean prevQRCodeScannerEnabled = mQRCodeScannerEnabled;
mQRCodeScannerEnabled = mSecureSettings.getIntForUser(LOCK_SCREEN_SHOW_QR_CODE_SCANNER, 0,
mUserTracker.getUserId()) != 0;
@@ -251,8 +260,15 @@ public class QRCodeScannerController implements
private void updateQRCodeScannerActivityDetails() {
String qrCodeScannerActivity = mDeviceConfigProxy.getString(
DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.DEFAULT_QR_CODE_SCANNER,
- mContext.getResources().getString(R.string.def_qr_code_component));
+ SystemUiDeviceConfigFlags.DEFAULT_QR_CODE_SCANNER, "");
+
+ // "" means either the flags is not available or is set to "", and in both the cases we
+ // want to use R.string.def_qr_code_component
+ if (Objects.equals(qrCodeScannerActivity, "")) {
+ qrCodeScannerActivity =
+ mContext.getResources().getString(R.string.def_qr_code_component);
+ }
+
String prevQrCodeScannerActivity = mQRCodeScannerActivity;
ComponentName componentName = null;
Intent intent = new Intent();
@@ -281,8 +297,12 @@ public class QRCodeScannerController implements
// Our intent should always be explicit and should have a component set
if (intent.getComponent() == null) return false;
- int flags = PackageManager.MATCH_DEFAULT_ONLY | PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+ int flags = PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+ | PackageManager.MATCH_UNINSTALLED_PACKAGES
+ | PackageManager.MATCH_DISABLED_COMPONENTS
+ | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
+ | PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS;
return !mContext.getPackageManager().queryIntentActivities(intent,
flags).isEmpty();
}
@@ -296,6 +316,11 @@ public class QRCodeScannerController implements
}
private void unregisterQRCodePreferenceObserver() {
+ if (!mConfigEnableLockScreenButton) {
+ // Settings only apply to lock screen entry point.
+ return;
+ }
+
mQRCodeScannerPreferenceObserver.forEach((key, value) -> {
mSecureSettings.unregisterContentObserver(value);
});
@@ -357,6 +382,11 @@ public class QRCodeScannerController implements
}
private void registerQRCodePreferenceObserver() {
+ if (!mConfigEnableLockScreenButton) {
+ // Settings only apply to lock screen entry point.
+ return;
+ }
+
int userId = mUserTracker.getUserId();
if (mQRCodeScannerPreferenceObserver.getOrDefault(userId, null) != null) return;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt
index e10e4d8a825c..7ac9205c7922 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt
@@ -56,7 +56,6 @@ import javax.inject.Named
*/
class FooterActionsController @Inject constructor(
view: FooterActionsView,
- private val qsPanelController: QSPanelController,
private val activityStarter: ActivityStarter,
private val userManager: UserManager,
private val userTracker: UserTracker,
@@ -82,7 +81,6 @@ class FooterActionsController @Inject constructor(
private val settingsButton: SettingsButton = view.findViewById(R.id.settings_button)
private val settingsButtonContainer: View? = view.findViewById(R.id.settings_button_container)
- private val editButton: View = view.findViewById(android.R.id.edit)
private val powerMenuLite: View = view.findViewById(R.id.pm_lite)
private val onUserInfoChangedListener = OnUserInfoChangedListener { _, picture, _ ->
@@ -176,13 +174,6 @@ class FooterActionsController @Inject constructor(
powerMenuLite.visibility = View.GONE
}
settingsButton.setOnClickListener(onClickListener)
- editButton.setOnClickListener(View.OnClickListener { view: View? ->
- if (falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
- return@OnClickListener
- }
- activityStarter.postQSRunnableDismissingKeyguard { qsPanelController.showEdit(view) }
- })
-
updateView()
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsControllerBuilder.kt b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsControllerBuilder.kt
index dd4dc87d8a9f..7694be51cba6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsControllerBuilder.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsControllerBuilder.kt
@@ -36,7 +36,6 @@ import javax.inject.Inject
import javax.inject.Named
class FooterActionsControllerBuilder @Inject constructor(
- private val qsPanelController: QSPanelController,
private val activityStarter: ActivityStarter,
private val userManager: UserManager,
private val userTracker: UserTracker,
@@ -66,7 +65,7 @@ class FooterActionsControllerBuilder @Inject constructor(
}
fun build(): FooterActionsController {
- return FooterActionsController(view, qsPanelController, activityStarter, userManager,
+ return FooterActionsController(view, activityStarter, userManager,
userTracker, userInfoController, multiUserSwitchControllerFactory.create(view),
deviceProvisionedController, falsingManager, metricsLogger, tunerService,
globalActionsDialog, uiEventLogger, showPMLiteButton, buttonsVisibleState,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsView.kt b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsView.kt
index f81f7bf73f64..e6fa2ae8dad1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsView.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsView.kt
@@ -43,7 +43,6 @@ class FooterActionsView(context: Context?, attrs: AttributeSet?) : LinearLayout(
private lateinit var multiUserSwitch: MultiUserSwitch
private lateinit var multiUserAvatar: ImageView
private lateinit var tunerIcon: View
- private lateinit var editTilesButton: View
private var settingsCogAnimator: TouchAnimator? = null
@@ -52,7 +51,6 @@ class FooterActionsView(context: Context?, attrs: AttributeSet?) : LinearLayout(
override fun onFinishInflate() {
super.onFinishInflate()
- editTilesButton = requireViewById(android.R.id.edit)
settingsButton = findViewById(R.id.settings_button)
settingsContainer = findViewById(R.id.settings_button_container)
multiUserSwitch = findViewById(R.id.multi_user_switch)
@@ -130,7 +128,6 @@ class FooterActionsView(context: Context?, attrs: AttributeSet?) : LinearLayout(
private fun updateClickabilities() {
multiUserSwitch.isClickable = multiUserSwitch.visibility == VISIBLE
- editTilesButton.isClickable = editTilesButton.visibility == VISIBLE
settingsButton.isClickable = settingsButton.visibility == VISIBLE
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java
index 066a286b271d..4622660a6c15 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java
@@ -47,6 +47,7 @@ public class QSFooterView extends FrameLayout {
private PageIndicator mPageIndicator;
private TextView mBuildText;
private View mActionsContainer;
+ private View mEditButton;
@Nullable
protected TouchAnimator mFooterAnimator;
@@ -79,6 +80,7 @@ public class QSFooterView extends FrameLayout {
mPageIndicator = findViewById(R.id.footer_page_indicator);
mActionsContainer = requireViewById(R.id.qs_footer_actions);
mBuildText = findViewById(R.id.build);
+ mEditButton = findViewById(android.R.id.edit);
updateResources();
setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
@@ -130,6 +132,7 @@ public class QSFooterView extends FrameLayout {
.addFloat(mActionsContainer, "alpha", 0, 1)
.addFloat(mPageIndicator, "alpha", 0, 1)
.addFloat(mBuildText, "alpha", 0, 1)
+ .addFloat(mEditButton, "alpha", 0, 1)
.setStartDelay(0.9f);
return builder.build();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
index e7c06e3c7ede..5327b7e3ba26 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
@@ -26,6 +26,8 @@ import android.widget.TextView;
import android.widget.Toast;
import com.android.systemui.R;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.qs.dagger.QSScope;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.util.ViewController;
@@ -45,10 +47,15 @@ public class QSFooterViewController extends ViewController<QSFooterView> impleme
private final FooterActionsController mFooterActionsController;
private final TextView mBuildText;
private final PageIndicator mPageIndicator;
+ private final View mEditButton;
+ private final FalsingManager mFalsingManager;
+ private final ActivityStarter mActivityStarter;
@Inject
QSFooterViewController(QSFooterView view,
UserTracker userTracker,
+ FalsingManager falsingManager,
+ ActivityStarter activityStarter,
QSPanelController qsPanelController,
QuickQSPanelController quickQSPanelController,
@Named(QS_FOOTER) FooterActionsController footerActionsController) {
@@ -57,9 +64,12 @@ public class QSFooterViewController extends ViewController<QSFooterView> impleme
mQsPanelController = qsPanelController;
mQuickQSPanelController = quickQSPanelController;
mFooterActionsController = footerActionsController;
+ mFalsingManager = falsingManager;
+ mActivityStarter = activityStarter;
mBuildText = mView.findViewById(R.id.build);
mPageIndicator = mView.findViewById(R.id.footer_page_indicator);
+ mEditButton = mView.findViewById(android.R.id.edit);
}
@Override
@@ -91,6 +101,14 @@ public class QSFooterViewController extends ViewController<QSFooterView> impleme
}
return false;
});
+
+ mEditButton.setOnClickListener(view -> {
+ if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
+ return;
+ }
+ mActivityStarter
+ .postQSRunnableDismissingKeyguard(() -> mQsPanelController.showEdit(view));
+ });
mQsPanelController.setFooterPageIndicator(mPageIndicator);
mView.updateEverything();
}
@@ -103,6 +121,7 @@ public class QSFooterViewController extends ViewController<QSFooterView> impleme
@Override
public void setVisibility(int visibility) {
mView.setVisibility(visibility);
+ mEditButton.setClickable(visibility == View.VISIBLE);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
index 596d8f01248a..e2964eaf2a23 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
@@ -129,17 +129,27 @@ public class UiModeNightTile extends QSTileImpl<QSTile.BooleanState> implements
? R.string.quick_settings_dark_mode_secondary_label_until_sunrise
: R.string.quick_settings_dark_mode_secondary_label_on_at_sunset);
} else if (uiMode == UiModeManager.MODE_NIGHT_CUSTOM) {
- final boolean use24HourFormat = android.text.format.DateFormat.is24HourFormat(mContext);
- final LocalTime time;
- if (nightMode) {
- time = mUiModeManager.getCustomNightModeEnd();
+ int nightModeCustomType = mUiModeManager.getNightModeCustomType();
+ if (nightModeCustomType == UiModeManager.MODE_NIGHT_CUSTOM_TYPE_SCHEDULE) {
+ final boolean use24HourFormat = android.text.format.DateFormat.is24HourFormat(
+ mContext);
+ final LocalTime time;
+ if (nightMode) {
+ time = mUiModeManager.getCustomNightModeEnd();
+ } else {
+ time = mUiModeManager.getCustomNightModeStart();
+ }
+ state.secondaryLabel = mContext.getResources().getString(nightMode
+ ? R.string.quick_settings_dark_mode_secondary_label_until
+ : R.string.quick_settings_dark_mode_secondary_label_on_at,
+ use24HourFormat ? time.toString() : formatter.format(time));
+ } else if (nightModeCustomType == UiModeManager.MODE_NIGHT_CUSTOM_TYPE_BEDTIME) {
+ state.secondaryLabel = mContext.getResources().getString(nightMode
+ ? R.string.quick_settings_dark_mode_secondary_label_until_bedtime_ends
+ : R.string.quick_settings_dark_mode_secondary_label_on_at_bedtime);
} else {
- time = mUiModeManager.getCustomNightModeStart();
+ state.secondaryLabel = null;
}
- state.secondaryLabel = mContext.getResources().getString(nightMode
- ? R.string.quick_settings_dark_mode_secondary_label_until
- : R.string.quick_settings_dark_mode_secondary_label_on_at,
- use24HourFormat ? time.toString() : formatter.format(time));
} else {
state.secondaryLabel = null;
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 60060aaf72da..00a314943f7a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -31,9 +31,9 @@ import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHE
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_SHELL_TRANSITIONS;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_SPLIT_SCREEN;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_STARTING_WINDOW;
-import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SMARTSPACE_TRANSITION_CONTROLLER;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SUPPORTS_WINDOW_CORNERS;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY;
+import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_UNLOCK_ANIMATION_CONTROLLER;
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;
@@ -83,6 +83,7 @@ import com.android.systemui.Dumpable;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.model.SysUiState;
import com.android.systemui.navigationbar.NavigationBar;
@@ -97,7 +98,6 @@ import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.QuickStepContract;
-import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.NotificationPanelViewController;
@@ -161,7 +161,7 @@ public class OverviewProxyService extends CurrentUserTracker implements
private final CommandQueue mCommandQueue;
private final ShellTransitions mShellTransitions;
private final Optional<StartingSurface> mStartingSurface;
- private final SmartspaceTransitionController mSmartspaceTransitionController;
+ private final KeyguardUnlockAnimationController mSysuiUnlockAnimationController;
private final Optional<RecentTasks> mRecentTasks;
private final UiEventLogger mUiEventLogger;
@@ -503,8 +503,8 @@ public class OverviewProxyService extends CurrentUserTracker implements
KEY_EXTRA_SHELL_STARTING_WINDOW,
startingwindow.createExternalInterface().asBinder()));
params.putBinder(
- KEY_EXTRA_SMARTSPACE_TRANSITION_CONTROLLER,
- mSmartspaceTransitionController.createExternalInterface().asBinder());
+ KEY_EXTRA_UNLOCK_ANIMATION_CONTROLLER,
+ mSysuiUnlockAnimationController.asBinder());
mRecentTasks.ifPresent(recentTasks -> params.putBinder(
KEY_EXTRA_RECENT_TASKS,
recentTasks.createExternalInterface().asBinder()));
@@ -570,8 +570,8 @@ public class OverviewProxyService extends CurrentUserTracker implements
BroadcastDispatcher broadcastDispatcher,
ShellTransitions shellTransitions,
ScreenLifecycle screenLifecycle,
- SmartspaceTransitionController smartspaceTransitionController,
UiEventLogger uiEventLogger,
+ KeyguardUnlockAnimationController sysuiUnlockAnimationController,
DumpManager dumpManager) {
super(broadcastDispatcher);
mContext = context;
@@ -644,7 +644,7 @@ public class OverviewProxyService extends CurrentUserTracker implements
updateEnabledState();
startConnectionToCurrentUser();
mStartingSurface = startingSurface;
- mSmartspaceTransitionController = smartspaceTransitionController;
+ mSysuiUnlockAnimationController = sysuiUnlockAnimationController;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/shared/system/smartspace/SmartspaceTransitionController.kt b/packages/SystemUI/src/com/android/systemui/shared/system/smartspace/SmartspaceTransitionController.kt
deleted file mode 100644
index 89b3df0f495f..000000000000
--- a/packages/SystemUI/src/com/android/systemui/shared/system/smartspace/SmartspaceTransitionController.kt
+++ /dev/null
@@ -1,143 +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.shared.system.smartspace
-
-import android.graphics.Rect
-import android.view.View
-import com.android.systemui.shared.system.ActivityManagerWrapper
-import com.android.systemui.shared.system.QuickStepContract
-import kotlin.math.min
-
-/**
- * Controller that keeps track of SmartSpace instances in remote processes (such as Launcher),
- * allowing System UI to query or update their state during shared-element transitions.
- */
-class SmartspaceTransitionController {
-
- /**
- * Implementation of [ISmartspaceTransitionController] that we provide to Launcher, allowing it
- * to provide us with a callback to query and update the state of its Smartspace.
- */
- private val ISmartspaceTransitionController = object : ISmartspaceTransitionController.Stub() {
- override fun setSmartspace(callback: ISmartspaceCallback?) {
- this@SmartspaceTransitionController.launcherSmartspace = callback
- updateLauncherSmartSpaceState()
- }
- }
-
- /**
- * Callback provided by Launcher to allow us to query and update the state of its SmartSpace.
- */
- public var launcherSmartspace: ISmartspaceCallback? = null
-
- public var lockscreenSmartspace: View? = null
-
- /**
- * Cached state of the Launcher SmartSpace. Retrieving the state is an IPC, so we should avoid
- * unnecessary
- */
- public var mLauncherSmartspaceState: SmartspaceState? = null
-
- /**
- * The bounds of our SmartSpace when the shared element transition began. We'll interpolate
- * between this and [smartspaceDestinationBounds] as the dismiss amount changes.
- */
- private val smartspaceOriginBounds = Rect()
-
- /** The bounds of the Launcher's SmartSpace, which is where we are animating our SmartSpace. */
-
- private val smartspaceDestinationBounds = Rect()
-
- fun createExternalInterface(): ISmartspaceTransitionController {
- return ISmartspaceTransitionController
- }
-
- /**
- * Updates [mLauncherSmartspaceState] and returns it. This will trigger a binder call, so use the
- * cached [mLauncherSmartspaceState] if possible.
- */
- fun updateLauncherSmartSpaceState(): SmartspaceState? {
- return launcherSmartspace?.smartspaceState.also {
- mLauncherSmartspaceState = it
- }
- }
-
- fun prepareForUnlockTransition() {
- updateLauncherSmartSpaceState().also { state ->
- if (state?.boundsOnScreen != null && lockscreenSmartspace != null) {
- lockscreenSmartspace!!.getBoundsOnScreen(smartspaceOriginBounds)
- with(smartspaceDestinationBounds) {
- set(state.boundsOnScreen)
- offset(-lockscreenSmartspace!!.paddingLeft,
- -lockscreenSmartspace!!.paddingTop)
- }
- }
- }
- }
-
- fun setProgressToDestinationBounds(progress: Float) {
- if (!isSmartspaceTransitionPossible()) {
- return
- }
-
- val progressClamped = min(1f, progress)
-
- // Calculate the distance (relative to the origin) that we need to be for the current
- // progress value.
- val progressX =
- (smartspaceDestinationBounds.left - smartspaceOriginBounds.left) * progressClamped
- val progressY =
- (smartspaceDestinationBounds.top - smartspaceOriginBounds.top) * progressClamped
-
- val lockscreenSmartspaceCurrentBounds = Rect().also {
- lockscreenSmartspace!!.getBoundsOnScreen(it)
- }
-
- // Figure out how far that is from our present location on the screen. This approach
- // compensates for the fact that our parent container is also translating to animate out.
- val dx = smartspaceOriginBounds.left + progressX -
- lockscreenSmartspaceCurrentBounds.left
- var dy = smartspaceOriginBounds.top + progressY -
- lockscreenSmartspaceCurrentBounds.top
-
- with(lockscreenSmartspace!!) {
- translationX = translationX + dx
- translationY = translationY + dy
- }
- }
-
- /**
- * Whether we're capable of performing the Smartspace shared element transition when we unlock.
- * This is true if:
- *
- * - The Launcher registered a Smartspace with us, it's reporting non-empty bounds on screen.
- * - Launcher is behind the keyguard, and the Smartspace is visible on the currently selected
- * page.
- */
- public fun isSmartspaceTransitionPossible(): Boolean {
- val smartSpaceNullOrBoundsEmpty = mLauncherSmartspaceState?.boundsOnScreen?.isEmpty ?: true
- return isLauncherUnderneath() && !smartSpaceNullOrBoundsEmpty
- }
-
- companion object {
- fun isLauncherUnderneath(): Boolean {
- return ActivityManagerWrapper.getInstance()
- .runningTask?.topActivity?.className?.equals(
- QuickStepContract.LAUNCHER_ACTIVITY_CLASS_NAME) ?: false
- }
- }
-} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
index c8115e2c197a..9a932bae833e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
@@ -355,7 +355,8 @@ class LightRevealScrim(context: Context?, attrs: AttributeSet?) : View(context,
}
override fun onDraw(canvas: Canvas?) {
- if (canvas == null || revealGradientWidth <= 0 || revealGradientHeight <= 0) {
+ if (canvas == null || revealGradientWidth <= 0 || revealGradientHeight <= 0
+ || revealAmount == 0f) {
if (revealAmount < 1f) {
canvas?.drawColor(revealGradientEndColor)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
index 46004db3067a..267ee6d2d177 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
@@ -154,6 +154,16 @@ class NotificationShadeDepthController @Inject constructor(
}
/**
+ * We're unlocking, and should not blur as the panel expansion changes.
+ */
+ var blursDisabledForUnlock: Boolean = false
+ set(value) {
+ if (field == value) return
+ field = value
+ scheduleUpdate()
+ }
+
+ /**
* Force stop blur effect when necessary.
*/
private var scrimsVisible: Boolean = false
@@ -192,7 +202,7 @@ class NotificationShadeDepthController @Inject constructor(
combinedBlur = max(combinedBlur, blurUtils.blurRadiusOfRatio(transitionToFullShadeProgress))
var shadeRadius = max(combinedBlur, wakeAndUnlockBlurRadius)
- if (blursDisabledForAppLaunch) {
+ if (blursDisabledForAppLaunch || blursDisabledForUnlock) {
shadeRadius = 0f
}
@@ -309,9 +319,7 @@ class NotificationShadeDepthController @Inject constructor(
/**
* Update blurs when pulling down the shade
*/
- override fun onPanelExpansionChanged(
- rawFraction: Float, expanded: Boolean, tracking: Boolean
- ) {
+ override fun onPanelExpansionChanged(rawFraction: Float, expanded: Boolean, tracking: Boolean) {
val timestamp = SystemClock.elapsedRealtimeNanos()
val expansion = MathUtils.saturate(
(rawFraction - panelPullDownMinFraction) / (1f - panelPullDownMinFraction))
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java
index ea90bdd940a7..6c3a9093fa98 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java
@@ -17,14 +17,10 @@
package com.android.systemui.statusbar;
import android.content.Context;
-import android.database.ContentObserver;
-import android.media.AudioAttributes;
import android.os.AsyncTask;
-import android.os.Handler;
-import android.os.UserHandle;
+import android.os.VibrationAttributes;
import android.os.VibrationEffect;
import android.os.Vibrator;
-import android.provider.Settings;
import com.android.systemui.dagger.SysUISingleton;
@@ -37,19 +33,8 @@ public class VibratorHelper {
private final Vibrator mVibrator;
private final Context mContext;
- private boolean mHapticFeedbackEnabled;
- private static final AudioAttributes STATUS_BAR_VIBRATION_ATTRIBUTES =
- new AudioAttributes.Builder()
- .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
- .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
- .build();
-
- final private ContentObserver mVibrationObserver = new ContentObserver(Handler.getMain()) {
- @Override
- public void onChange(boolean selfChange) {
- updateHapticFeedBackEnabled();
- }
- };
+ private static final VibrationAttributes TOUCH_VIBRATION_ATTRIBUTES =
+ VibrationAttributes.createForUsage(VibrationAttributes.USAGE_TOUCH);
/**
*/
@@ -57,23 +42,11 @@ public class VibratorHelper {
public VibratorHelper(Context context) {
mContext = context;
mVibrator = context.getSystemService(Vibrator.class);
-
- mContext.getContentResolver().registerContentObserver(
- Settings.System.getUriFor(Settings.System.HAPTIC_FEEDBACK_ENABLED), true,
- mVibrationObserver);
- mVibrationObserver.onChange(false /* selfChange */);
}
public void vibrate(final int effectId) {
- if (mHapticFeedbackEnabled) {
- AsyncTask.execute(() ->
- mVibrator.vibrate(VibrationEffect.get(effectId, false /* fallback */),
- STATUS_BAR_VIBRATION_ATTRIBUTES));
- }
- }
-
- private void updateHapticFeedBackEnabled() {
- mHapticFeedbackEnabled = Settings.System.getIntForUser(mContext.getContentResolver(),
- Settings.System.HAPTIC_FEEDBACK_ENABLED, 0, UserHandle.USER_CURRENT) != 0;
+ AsyncTask.execute(() ->
+ mVibrator.vibrate(VibrationEffect.get(effectId, false /* fallback */),
+ TOUCH_VIBRATION_ATTRIBUTES));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
index 3449bd8e2686..5aeab84b677c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
@@ -23,7 +23,6 @@ import android.graphics.drawable.AnimatedImageDrawable
import android.os.Handler
import android.service.notification.NotificationListenerService.Ranking
import android.service.notification.NotificationListenerService.RankingMap
-import com.android.internal.statusbar.NotificationVisibility
import com.android.internal.widget.ConversationLayout
import com.android.internal.widget.MessagingImageMessage
import com.android.internal.widget.MessagingLayout
@@ -31,6 +30,7 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.inflation.BindEventManager
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
@@ -72,7 +72,8 @@ class ConversationNotificationProcessor @Inject constructor(
*/
@SysUISingleton
class AnimatedImageNotificationManager @Inject constructor(
- private val notificationEntryManager: NotificationEntryManager,
+ private val notifCollection: CommonNotifCollection,
+ private val bindEventManager: BindEventManager,
private val headsUpManager: HeadsUpManager,
private val statusBarStateController: StatusBarStateController
) {
@@ -83,33 +84,23 @@ class AnimatedImageNotificationManager @Inject constructor(
fun bind() {
headsUpManager.addListener(object : OnHeadsUpChangedListener {
override fun onHeadsUpStateChanged(entry: NotificationEntry, isHeadsUp: Boolean) {
- entry.row?.let { row ->
- updateAnimatedImageDrawables(row, animating = isHeadsUp || isStatusBarExpanded)
- }
+ updateAnimatedImageDrawables(entry)
}
})
statusBarStateController.addCallback(object : StatusBarStateController.StateListener {
override fun onExpandedChanged(isExpanded: Boolean) {
isStatusBarExpanded = isExpanded
- notificationEntryManager.activeNotificationsForCurrentUser.forEach { entry ->
- entry.row?.let { row ->
- updateAnimatedImageDrawables(row, animating = isExpanded || row.isHeadsUp)
- }
- }
+ notifCollection.allNotifs.forEach(::updateAnimatedImageDrawables)
}
})
- notificationEntryManager.addNotificationEntryListener(object : NotificationEntryListener {
- override fun onEntryInflated(entry: NotificationEntry) {
- entry.row?.let { row ->
- updateAnimatedImageDrawables(
- row,
- animating = isStatusBarExpanded || row.isHeadsUp)
- }
- }
- override fun onEntryReinflated(entry: NotificationEntry) = onEntryInflated(entry)
- })
+ bindEventManager.addListener(::updateAnimatedImageDrawables)
}
+ private fun updateAnimatedImageDrawables(entry: NotificationEntry) =
+ entry.row?.let { row ->
+ updateAnimatedImageDrawables(row, animating = row.isHeadsUp || isStatusBarExpanded)
+ }
+
private fun updateAnimatedImageDrawables(row: ExpandableNotificationRow, animating: Boolean) =
(row.layouts?.asSequence() ?: emptySequence())
.flatMap { layout -> layout.allViews.asSequence() }
@@ -138,7 +129,7 @@ class AnimatedImageNotificationManager @Inject constructor(
*/
@SysUISingleton
class ConversationNotificationManager @Inject constructor(
- private val notificationEntryManager: NotificationEntryManager,
+ private val bindEventManager: BindEventManager,
private val notificationGroupManager: NotificationGroupManagerLegacy,
private val context: Context,
private val notifCollection: CommonNotifCollection,
@@ -151,35 +142,12 @@ class ConversationNotificationManager @Inject constructor(
private var notifPanelCollapsed = true
- private val entryManagerListener = object : NotificationEntryListener {
- override fun onNotificationRankingUpdated(rankingMap: RankingMap) =
- updateNotificationRanking(rankingMap)
- override fun onEntryInflated(entry: NotificationEntry) =
- onEntryViewBound(entry)
- override fun onEntryReinflated(entry: NotificationEntry) = onEntryInflated(entry)
- override fun onEntryRemoved(
- entry: NotificationEntry,
- visibility: NotificationVisibility?,
- removedByUser: Boolean,
- reason: Int
- ) = removeTrackedEntry(entry)
- }
-
- private val notifCollectionListener = object : NotifCollectionListener {
- override fun onRankingUpdate(ranking: RankingMap) =
- updateNotificationRanking(ranking)
-
- override fun onEntryRemoved(entry: NotificationEntry, reason: Int) {
- removeTrackedEntry(entry)
- }
- }
-
private fun updateNotificationRanking(rankingMap: RankingMap) {
fun getLayouts(view: NotificationContentView) =
sequenceOf(view.contractedChild, view.expandedChild, view.headsUpChild)
val ranking = Ranking()
val activeConversationEntries = states.keys.asSequence()
- .mapNotNull { notificationEntryManager.getActiveNotificationUnfiltered(it) }
+ .mapNotNull { notifCollection.getEntry(it) }
for (entry in activeConversationEntries) {
if (rankingMap.getRanking(entry.sbn.key, ranking) && ranking.isConversation) {
val important = ranking.channel.isImportantConversation
@@ -204,7 +172,7 @@ class ConversationNotificationManager @Inject constructor(
layout.setIsImportantConversation(important, false)
}
}
- if (changed) {
+ if (changed && !featureFlags.isNewPipelineEnabled()) {
notificationGroupManager.updateIsolation(entry)
}
}
@@ -233,11 +201,14 @@ class ConversationNotificationManager @Inject constructor(
}
init {
- if (featureFlags.isNewPipelineEnabled()) {
- notifCollection.addCollectionListener(notifCollectionListener)
- } else {
- notificationEntryManager.addNotificationEntryListener(entryManagerListener)
- }
+ notifCollection.addCollectionListener(object : NotifCollectionListener {
+ override fun onRankingUpdate(ranking: RankingMap) =
+ updateNotificationRanking(ranking)
+
+ override fun onEntryRemoved(entry: NotificationEntry, reason: Int) =
+ removeTrackedEntry(entry)
+ })
+ bindEventManager.addListener(::onEntryViewBound)
}
private fun ConversationState.shouldIncrementUnread(newBuilder: Notification.Builder) =
@@ -265,11 +236,10 @@ class ConversationNotificationManager @Inject constructor(
val expanded = states
.asSequence()
.mapNotNull { (key, _) ->
- notificationEntryManager.getActiveNotificationUnfiltered(key)
- ?.let { entry ->
- if (entry.row?.isExpanded == true) key to entry
- else null
- }
+ notifCollection.getEntry(key)?.let { entry ->
+ if (entry.row?.isExpanded == true) key to entry
+ else null
+ }
}
.toMap()
states.replaceAll { key, state ->
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java
index 97ae83ef3f6a..643deb7463b5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.notification;
import android.annotation.Nullable;
+import android.app.NotificationChannel;
+import android.os.UserHandle;
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.StatusBarNotification;
@@ -106,4 +108,20 @@ public interface NotificationEntryListener {
*/
default void onNotificationRankingUpdated(RankingMap rankingMap) {
}
+
+ /**
+ * Called when a notification channel is modified, in response to
+ * {@link NotificationListenerService#onNotificationChannelModified}.
+ *
+ * @param pkgName the package the notification channel belongs to.
+ * @param user the user the notification channel belongs to.
+ * @param channel the channel being modified.
+ * @param modificationType the type of modification that occurred to the channel.
+ */
+ default void onNotificationChannelModified(
+ String pkgName,
+ UserHandle user,
+ NotificationChannel channel,
+ int modificationType) {
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index 062e2395f3dd..c33160858d0f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -21,8 +21,10 @@ import static com.android.systemui.statusbar.notification.collection.NotifCollec
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationCallback;
import android.app.Notification;
+import android.app.NotificationChannel;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.os.UserHandle;
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.NotificationListenerService.RankingMap;
@@ -402,6 +404,15 @@ public class NotificationEntryManager implements
@Override
public void onNotificationsInitialized() {
}
+
+ @Override
+ public void onNotificationChannelModified(
+ String pkgName,
+ UserHandle user,
+ NotificationChannel channel,
+ int modificationType) {
+ notifyChannelModified(pkgName, user, channel, modificationType);
+ }
};
/**
@@ -779,6 +790,19 @@ public class NotificationEntryManager implements
}
}
+ void notifyChannelModified(
+ String pkgName,
+ UserHandle user,
+ NotificationChannel channel,
+ int modificationType) {
+ for (NotifCollectionListener listener : mNotifCollectionListeners) {
+ listener.onNotificationChannelModified(pkgName, user, channel, modificationType);
+ }
+ for (NotificationEntryListener listener : mNotificationEntryListeners) {
+ listener.onNotificationChannelModified(pkgName, user, channel, modificationType);
+ }
+ }
+
private void updateRankingOfPendingNotifications(@Nullable RankingMap rankingMap) {
if (rankingMap == null) {
return;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/SectionHeaderVisibilityProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/SectionHeaderVisibilityProvider.kt
new file mode 100644
index 000000000000..03b978e7784c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/SectionHeaderVisibilityProvider.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification
+
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+
+/**
+ * A class which keeps track of whether section headers should be shown in the notification shade.
+ *
+ * (In an ideal world, this would directly monitor the state of the keyguard and invalidate the
+ * pipeline to show/hide headers, but the KeyguardController already invalidates the pipeline when
+ * the keyguard's state changes. Instead of having both classes monitor for state changes and ending
+ * up with duplicate runs of the pipeline, we let the KeyguardController update the header
+ * visibility when it invalidates, and we just store that state here.)
+ */
+@SysUISingleton
+class SectionHeaderVisibilityProvider @Inject constructor() {
+ var sectionHeadersVisible = true
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
index 760556131c60..2a2cc81c3223 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
@@ -46,6 +46,7 @@ import android.annotation.IntDef;
import android.annotation.MainThread;
import android.annotation.UserIdInt;
import android.app.Notification;
+import android.app.NotificationChannel;
import android.os.Handler;
import android.os.RemoteException;
import android.os.Trace;
@@ -72,6 +73,7 @@ import com.android.systemui.statusbar.notification.collection.coalescer.Coalesce
import com.android.systemui.statusbar.notification.collection.coalescer.GroupCoalescer;
import com.android.systemui.statusbar.notification.collection.coalescer.GroupCoalescer.BatchableNotificationHandler;
import com.android.systemui.statusbar.notification.collection.notifcollection.BindEntryEvent;
+import com.android.systemui.statusbar.notification.collection.notifcollection.ChannelChangedEvent;
import com.android.systemui.statusbar.notification.collection.notifcollection.CleanUpEntryEvent;
import com.android.systemui.statusbar.notification.collection.notifcollection.CollectionReadyForBuildListener;
import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
@@ -424,6 +426,16 @@ public class NotifCollection implements Dumpable {
dispatchEventsAndRebuildList();
}
+ private void onNotificationChannelModified(
+ String pkgName,
+ UserHandle user,
+ NotificationChannel channel,
+ int modificationType) {
+ Assert.isMainThread();
+ mEventQueue.add(new ChannelChangedEvent(pkgName, user, channel, modificationType));
+ dispatchEventsAndRebuildList();
+ }
+
private void onNotificationsInitialized() {
mInitializedTimestamp = mClock.uptimeMillis();
}
@@ -835,6 +847,19 @@ public class NotifCollection implements Dumpable {
}
@Override
+ public void onNotificationChannelModified(
+ String pkgName,
+ UserHandle user,
+ NotificationChannel channel,
+ int modificationType) {
+ NotifCollection.this.onNotificationChannelModified(
+ pkgName,
+ user,
+ channel,
+ modificationType);
+ }
+
+ @Override
public void onNotificationsInitialized() {
NotifCollection.this.onNotificationsInitialized();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java
index 09ae7eb38a06..87e531c01a5f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java
@@ -260,7 +260,8 @@ public class GroupCoalescer implements Dumpable {
}
events.sort(mEventComparator);
- mLogger.logEmitBatch(batch.mGroupKey);
+ long batchAge = mClock.uptimeMillis() - batch.mCreatedTimestamp;
+ mLogger.logEmitBatch(batch.mGroupKey, batch.mMembers.size(), batchAge);
mHandler.onNotificationBatchPosted(events);
}
@@ -337,6 +338,6 @@ public class GroupCoalescer implements Dumpable {
void onNotificationBatchPosted(List<CoalescedEvent> events);
}
- private static final int MIN_GROUP_LINGER_DURATION = 50;
+ private static final int MIN_GROUP_LINGER_DURATION = 200;
private static final int MAX_GROUP_LINGER_DURATION = 500;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerLogger.kt
index d4d5b64240c2..211e37473a70 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerLogger.kt
@@ -32,11 +32,13 @@ class GroupCoalescerLogger @Inject constructor(
})
}
- fun logEmitBatch(groupKey: String) {
+ fun logEmitBatch(groupKey: String, batchSize: Int, batchAgeMs: Long) {
buffer.log(TAG, LogLevel.DEBUG, {
str1 = groupKey
+ int1 = batchSize
+ long1 = batchAgeMs
}, {
- "Emitting event batch for group $str1"
+ "Emitting batch for group $str1 size=$int1 age=${long1}ms"
})
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java
index 3a39c39cfb20..f04b24ebfb42 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java
@@ -101,7 +101,7 @@ public class AppOpsCoordinator implements Coordinator {
};
/**
- * Puts foreground service notifications into its own section.
+ * Puts colorized foreground service and call notifications into its own section.
*/
private final NotifSectioner mNotifSectioner = new NotifSectioner("ForegroundService",
NotificationPriorityBucketKt.BUCKET_FOREGROUND_SERVICE) {
@@ -109,12 +109,22 @@ public class AppOpsCoordinator implements Coordinator {
public boolean isInSection(ListEntry entry) {
NotificationEntry notificationEntry = entry.getRepresentativeEntry();
if (notificationEntry != null) {
- Notification notification = notificationEntry.getSbn().getNotification();
- return notification.isForegroundService()
- && notification.isColorized()
- && entry.getRepresentativeEntry().getImportance() > IMPORTANCE_MIN;
+ return isColorizedForegroundService(notificationEntry) || isCall(notificationEntry);
}
return false;
}
+
+ private boolean isColorizedForegroundService(NotificationEntry entry) {
+ Notification notification = entry.getSbn().getNotification();
+ return notification.isForegroundService()
+ && notification.isColorized()
+ && entry.getImportance() > IMPORTANCE_MIN;
+ }
+
+ private boolean isCall(NotificationEntry entry) {
+ Notification notification = entry.getSbn().getNotification();
+ return entry.getImportance() > IMPORTANCE_MIN
+ && notification.isStyle(Notification.CallStyle.class);
+ }
};
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt
index e59f4a62f9b7..ba88ad7844f1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt
@@ -20,11 +20,13 @@ import com.android.systemui.statusbar.notification.collection.ListEntry
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifComparator
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner
import com.android.systemui.statusbar.notification.collection.render.NodeController
import com.android.systemui.statusbar.notification.dagger.PeopleHeader
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.PeopleNotificationType
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_NON_PERSON
import com.android.systemui.statusbar.notification.stack.BUCKET_PEOPLE
import javax.inject.Inject
@@ -48,18 +50,36 @@ class ConversationCoordinator @Inject constructor(
val sectioner = object : NotifSectioner("People", BUCKET_PEOPLE) {
override fun isInSection(entry: ListEntry): Boolean =
- isConversation(entry.representativeEntry!!)
+ isConversation(entry)
override fun getHeaderNodeController() =
// TODO: remove SHOW_ALL_SECTIONS, this redundant method, and peopleHeaderController
if (RankingCoordinator.SHOW_ALL_SECTIONS) peopleHeaderController else null
}
+ val comparator = object : NotifComparator("People") {
+ override fun compare(entry1: ListEntry, entry2: ListEntry): Int {
+ assert(entry1.section === entry2.section)
+ if (entry1.section?.sectioner !== sectioner) {
+ return 0
+ }
+ val type1 = getPeopleType(entry1)
+ val type2 = getPeopleType(entry2)
+ return type2.compareTo(type1)
+ }
+ }
+
override fun attach(pipeline: NotifPipeline) {
pipeline.addPromoter(notificationPromoter)
}
- private fun isConversation(entry: NotificationEntry): Boolean =
- peopleNotificationIdentifier.getPeopleNotificationType(entry) != TYPE_NON_PERSON
+ private fun isConversation(entry: ListEntry): Boolean =
+ getPeopleType(entry) != TYPE_NON_PERSON
+
+ @PeopleNotificationType
+ private fun getPeopleType(entry: ListEntry): Int =
+ entry.representativeEntry?.let {
+ peopleNotificationIdentifier.getPeopleNotificationType(it)
+ } ?: TYPE_NON_PERSON
companion object {
private const val TAG = "ConversationCoordinator"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java
deleted file mode 100644
index 74109120149e..000000000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java
+++ /dev/null
@@ -1,259 +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.systemui.statusbar.notification.collection.coordinator;
-
-import static com.android.systemui.statusbar.NotificationRemoteInputManager.FORCE_REMOTE_INPUT_HISTORY;
-import static com.android.systemui.statusbar.notification.interruption.HeadsUpController.alertAgain;
-
-import android.util.ArraySet;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.statusbar.NotificationRemoteInputManager;
-import com.android.systemui.statusbar.notification.collection.ListEntry;
-import com.android.systemui.statusbar.notification.collection.NotifPipeline;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope;
-import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter;
-import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner;
-import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
-import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
-import com.android.systemui.statusbar.notification.collection.render.NodeController;
-import com.android.systemui.statusbar.notification.dagger.IncomingHeader;
-import com.android.systemui.statusbar.notification.interruption.HeadsUpViewBinder;
-import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
-import com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
-import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
-import com.android.systemui.util.concurrency.DelayableExecutor;
-
-import javax.inject.Inject;
-
-/**
- * Coordinates heads up notification (HUN) interactions with the notification pipeline based on
- * the HUN state reported by the {@link HeadsUpManager}. In this class we only consider one
- * notification, in particular the {@link HeadsUpManager#getTopEntry()}, to be HeadsUpping at a
- * time even though other notifications may be queued to heads up next.
- *
- * The current HUN, but not HUNs that are queued to heads up, will be:
- * - Lifetime extended until it's no longer heads upping.
- * - Promoted out of its group if it's a child of a group.
- * - In the HeadsUpCoordinatorSection. Ordering is configured in {@link NotifCoordinators}.
- * - Removed from HeadsUpManager if it's removed from the NotificationCollection.
- *
- * Note: The inflation callback in {@link PreparationCoordinator} handles showing HUNs.
- */
-@CoordinatorScope
-public class HeadsUpCoordinator implements Coordinator {
- private static final String TAG = "HeadsUpCoordinator";
-
- private final HeadsUpManager mHeadsUpManager;
- private final HeadsUpViewBinder mHeadsUpViewBinder;
- private final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
- private final NotificationRemoteInputManager mRemoteInputManager;
- private final NodeController mIncomingHeaderController;
- private final DelayableExecutor mExecutor;
-
- private NotifLifetimeExtender.OnEndLifetimeExtensionCallback mEndLifetimeExtension;
- // notifs we've extended the lifetime for
- private final ArraySet<NotificationEntry> mNotifsExtendingLifetime = new ArraySet<>();
-
- @Inject
- public HeadsUpCoordinator(
- HeadsUpManager headsUpManager,
- HeadsUpViewBinder headsUpViewBinder,
- NotificationInterruptStateProvider notificationInterruptStateProvider,
- NotificationRemoteInputManager remoteInputManager,
- @IncomingHeader NodeController incomingHeaderController,
- @Main DelayableExecutor executor) {
- mHeadsUpManager = headsUpManager;
- mHeadsUpViewBinder = headsUpViewBinder;
- mNotificationInterruptStateProvider = notificationInterruptStateProvider;
- mRemoteInputManager = remoteInputManager;
- mIncomingHeaderController = incomingHeaderController;
- mExecutor = executor;
- }
-
- @Override
- public void attach(NotifPipeline pipeline) {
- mHeadsUpManager.addListener(mOnHeadsUpChangedListener);
- pipeline.addCollectionListener(mNotifCollectionListener);
- pipeline.addPromoter(mNotifPromoter);
- pipeline.addNotificationLifetimeExtender(mLifetimeExtender);
- }
-
- public NotifSectioner getSectioner() {
- return mNotifSectioner;
- }
-
- private void onHeadsUpViewBound(NotificationEntry entry) {
- mHeadsUpManager.showNotification(entry);
- }
-
- private final NotifCollectionListener mNotifCollectionListener = new NotifCollectionListener() {
- /**
- * Notification was just added and if it should heads up, bind the view and then show it.
- */
- @Override
- public void onEntryAdded(NotificationEntry entry) {
- if (mNotificationInterruptStateProvider.shouldHeadsUp(entry)) {
- mHeadsUpViewBinder.bindHeadsUpView(
- entry,
- HeadsUpCoordinator.this::onHeadsUpViewBound);
- }
- }
-
- /**
- * Notification could've updated to be heads up or not heads up. Even if it did update to
- * heads up, if the notification specified that it only wants to alert once, don't heads
- * up again.
- */
- @Override
- public void onEntryUpdated(NotificationEntry entry) {
- boolean hunAgain = alertAgain(entry, entry.getSbn().getNotification());
- // includes check for whether this notification should be filtered:
- boolean shouldHeadsUp = mNotificationInterruptStateProvider.shouldHeadsUp(entry);
- final boolean wasHeadsUp = mHeadsUpManager.isAlerting(entry.getKey());
- if (wasHeadsUp) {
- if (shouldHeadsUp) {
- mHeadsUpManager.updateNotification(entry.getKey(), hunAgain);
- } else if (!mHeadsUpManager.isEntryAutoHeadsUpped(entry.getKey())) {
- // We don't want this to be interrupting anymore, let's remove it
- mHeadsUpManager.removeNotification(
- entry.getKey(), false /* removeImmediately */);
- }
- } else if (shouldHeadsUp && hunAgain) {
- // This notification was updated to be heads up, show it!
- mHeadsUpViewBinder.bindHeadsUpView(
- entry,
- HeadsUpCoordinator.this::onHeadsUpViewBound);
- }
- }
-
- /**
- * Stop alerting HUNs that are removed from the notification collection
- */
- @Override
- public void onEntryRemoved(NotificationEntry entry, int reason) {
- final String entryKey = entry.getKey();
- if (mHeadsUpManager.isAlerting(entryKey)) {
- boolean removeImmediatelyForRemoteInput =
- mRemoteInputManager.isSpinning(entryKey)
- && !FORCE_REMOTE_INPUT_HISTORY;
- mHeadsUpManager.removeNotification(entry.getKey(), removeImmediatelyForRemoteInput);
- }
- }
-
- @Override
- public void onEntryCleanUp(NotificationEntry entry) {
- mHeadsUpViewBinder.abortBindCallback(entry);
- }
- };
-
- private final NotifLifetimeExtender mLifetimeExtender = new NotifLifetimeExtender() {
- @Override
- public @NonNull String getName() {
- return TAG;
- }
-
- @Override
- public void setCallback(@NonNull OnEndLifetimeExtensionCallback callback) {
- mEndLifetimeExtension = callback;
- }
-
- @Override
- public boolean maybeExtendLifetime(@NonNull NotificationEntry entry, int reason) {
- boolean extend = !mHeadsUpManager.canRemoveImmediately(entry.getKey());
- if (extend) {
- if (isSticky(entry)) {
- long removeAfterMillis = mHeadsUpManager.getEarliestRemovalTime(entry.getKey());
- mExecutor.executeDelayed(() -> {
- if (mNotifsExtendingLifetime.contains(entry)
- && mHeadsUpManager.canRemoveImmediately(entry.getKey())) {
- mHeadsUpManager.removeNotification(
- entry.getKey(), /* releaseImmediately */ true);
- }
- }, removeAfterMillis);
- } else {
- // remove as early as possible
- mExecutor.execute(
- () -> mHeadsUpManager.removeNotification(
- entry.getKey(), /* releaseImmediately */ false));
- }
- mNotifsExtendingLifetime.add(entry);
- }
- return extend;
- }
-
- @Override
- public void cancelLifetimeExtension(@NonNull NotificationEntry entry) {
- mNotifsExtendingLifetime.remove(entry);
- }
- };
-
- private final NotifPromoter mNotifPromoter = new NotifPromoter(TAG) {
- @Override
- public boolean shouldPromoteToTopLevel(NotificationEntry entry) {
- return isCurrentlyShowingHun(entry);
- }
- };
-
- private final NotifSectioner mNotifSectioner = new NotifSectioner("HeadsUp",
- NotificationPriorityBucketKt.BUCKET_HEADS_UP) {
- @Override
- public boolean isInSection(ListEntry entry) {
- return isCurrentlyShowingHun(entry);
- }
-
- @Nullable
- @Override
- public NodeController getHeaderNodeController() {
- // TODO: remove SHOW_ALL_SECTIONS, this redundant method, and mIncomingHeaderController
- if (RankingCoordinator.SHOW_ALL_SECTIONS) {
- return mIncomingHeaderController;
- }
- return null;
- }
- };
-
- private final OnHeadsUpChangedListener mOnHeadsUpChangedListener =
- new OnHeadsUpChangedListener() {
- @Override
- public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
- if (!isHeadsUp) {
- mHeadsUpViewBinder.unbindHeadsUpView(entry);
- endNotifLifetimeExtensionIfExtended(entry);
- }
- }
- };
-
- private boolean isSticky(NotificationEntry entry) {
- return mHeadsUpManager.isSticky(entry.getKey());
- }
-
- private boolean isCurrentlyShowingHun(ListEntry entry) {
- return mHeadsUpManager.isAlerting(entry.getKey());
- }
-
- private void endNotifLifetimeExtensionIfExtended(NotificationEntry entry) {
- if (mNotifsExtendingLifetime.remove(entry)) {
- mEndLifetimeExtension.onEndLifetimeExtension(mLifetimeExtender, entry);
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt
new file mode 100644
index 000000000000..b84b38233073
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar.notification.collection.coordinator
+
+import android.util.ArraySet
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.statusbar.NotificationRemoteInputManager
+import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.NotifPipeline
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender.OnEndLifetimeExtensionCallback
+import com.android.systemui.statusbar.notification.collection.render.NodeController
+import com.android.systemui.statusbar.notification.dagger.IncomingHeader
+import com.android.systemui.statusbar.notification.interruption.HeadsUpController
+import com.android.systemui.statusbar.notification.interruption.HeadsUpViewBinder
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider
+import com.android.systemui.statusbar.notification.stack.BUCKET_HEADS_UP
+import com.android.systemui.statusbar.policy.HeadsUpManager
+import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener
+import com.android.systemui.util.concurrency.DelayableExecutor
+import javax.inject.Inject
+
+/**
+ * Coordinates heads up notification (HUN) interactions with the notification pipeline based on
+ * the HUN state reported by the [HeadsUpManager]. In this class we only consider one
+ * notification, in particular the [HeadsUpManager.getTopEntry], to be HeadsUpping at a
+ * time even though other notifications may be queued to heads up next.
+ *
+ * The current HUN, but not HUNs that are queued to heads up, will be:
+ * - Lifetime extended until it's no longer heads upping.
+ * - Promoted out of its group if it's a child of a group.
+ * - In the HeadsUpCoordinatorSection. Ordering is configured in [NotifCoordinators].
+ * - Removed from HeadsUpManager if it's removed from the NotificationCollection.
+ *
+ * Note: The inflation callback in [PreparationCoordinator] handles showing HUNs.
+ */
+@CoordinatorScope
+class HeadsUpCoordinator @Inject constructor(
+ private val mHeadsUpManager: HeadsUpManager,
+ private val mHeadsUpViewBinder: HeadsUpViewBinder,
+ private val mNotificationInterruptStateProvider: NotificationInterruptStateProvider,
+ private val mRemoteInputManager: NotificationRemoteInputManager,
+ @IncomingHeader private val mIncomingHeaderController: NodeController,
+ @Main private val mExecutor: DelayableExecutor
+) : Coordinator {
+ private var mEndLifetimeExtension: OnEndLifetimeExtensionCallback? = null
+
+ // notifs we've extended the lifetime for
+ private val mNotifsExtendingLifetime = ArraySet<NotificationEntry>()
+
+ override fun attach(pipeline: NotifPipeline) {
+ mHeadsUpManager.addListener(mOnHeadsUpChangedListener)
+ pipeline.addCollectionListener(mNotifCollectionListener)
+ pipeline.addPromoter(mNotifPromoter)
+ pipeline.addNotificationLifetimeExtender(mLifetimeExtender)
+ }
+
+ private fun onHeadsUpViewBound(entry: NotificationEntry) {
+ mHeadsUpManager.showNotification(entry)
+ }
+
+ private val mNotifCollectionListener = object : NotifCollectionListener {
+ /**
+ * Notification was just added and if it should heads up, bind the view and then show it.
+ */
+ override fun onEntryAdded(entry: NotificationEntry) {
+ if (mNotificationInterruptStateProvider.shouldHeadsUp(entry)) {
+ mHeadsUpViewBinder.bindHeadsUpView(entry) { entry -> onHeadsUpViewBound(entry) }
+ }
+ }
+
+ /**
+ * Notification could've updated to be heads up or not heads up. Even if it did update to
+ * heads up, if the notification specified that it only wants to alert once, don't heads
+ * up again.
+ */
+ override fun onEntryUpdated(entry: NotificationEntry) {
+ val hunAgain = HeadsUpController.alertAgain(entry, entry.sbn.notification)
+ // includes check for whether this notification should be filtered:
+ val shouldHeadsUp = mNotificationInterruptStateProvider.shouldHeadsUp(entry)
+ val wasHeadsUp = mHeadsUpManager.isAlerting(entry.key)
+ if (wasHeadsUp) {
+ if (shouldHeadsUp) {
+ mHeadsUpManager.updateNotification(entry.key, hunAgain)
+ } else if (!mHeadsUpManager.isEntryAutoHeadsUpped(entry.key)) {
+ // We don't want this to be interrupting anymore, let's remove it
+ mHeadsUpManager.removeNotification(
+ entry.key, false /* removeImmediately */
+ )
+ }
+ } else if (shouldHeadsUp && hunAgain) {
+ // This notification was updated to be heads up, show it!
+ mHeadsUpViewBinder.bindHeadsUpView(entry) { entry -> onHeadsUpViewBound(entry) }
+ }
+ }
+
+ /**
+ * Stop alerting HUNs that are removed from the notification collection
+ */
+ override fun onEntryRemoved(entry: NotificationEntry, reason: Int) {
+ val entryKey = entry.key
+ if (mHeadsUpManager.isAlerting(entryKey)) {
+ val removeImmediatelyForRemoteInput = (mRemoteInputManager.isSpinning(entryKey) &&
+ !NotificationRemoteInputManager.FORCE_REMOTE_INPUT_HISTORY)
+ mHeadsUpManager.removeNotification(entry.key, removeImmediatelyForRemoteInput)
+ }
+ }
+
+ override fun onEntryCleanUp(entry: NotificationEntry) {
+ mHeadsUpViewBinder.abortBindCallback(entry)
+ }
+ }
+
+ private val mLifetimeExtender = object : NotifLifetimeExtender {
+ override fun getName() = TAG
+
+ override fun setCallback(callback: OnEndLifetimeExtensionCallback) {
+ mEndLifetimeExtension = callback
+ }
+
+ override fun maybeExtendLifetime(entry: NotificationEntry, reason: Int): Boolean {
+ if (mHeadsUpManager.canRemoveImmediately(entry.key)) {
+ return false
+ }
+ if (isSticky(entry)) {
+ val removeAfterMillis = mHeadsUpManager.getEarliestRemovalTime(entry.key)
+ mExecutor.executeDelayed({
+ val canStillRemove = mHeadsUpManager.canRemoveImmediately(entry.key)
+ if (mNotifsExtendingLifetime.contains(entry) && canStillRemove) {
+ mHeadsUpManager.removeNotification(entry.key, /* releaseImmediately */ true)
+ }
+ }, removeAfterMillis)
+ } else {
+ mExecutor.execute {
+ mHeadsUpManager.removeNotification(entry.key, /* releaseImmediately */ false)
+ }
+ }
+ mNotifsExtendingLifetime.add(entry)
+ return true
+ }
+
+ override fun cancelLifetimeExtension(entry: NotificationEntry) {
+ mNotifsExtendingLifetime.remove(entry)
+ }
+ }
+
+ private val mNotifPromoter = object : NotifPromoter(TAG) {
+ override fun shouldPromoteToTopLevel(entry: NotificationEntry): Boolean =
+ isCurrentlyShowingHun(entry)
+ }
+
+ val sectioner = object : NotifSectioner("HeadsUp", BUCKET_HEADS_UP) {
+ override fun isInSection(entry: ListEntry): Boolean = isCurrentlyShowingHun(entry)
+
+ override fun getHeaderNodeController(): NodeController? =
+ // TODO: remove SHOW_ALL_SECTIONS, this redundant method, and mIncomingHeaderController
+ if (RankingCoordinator.SHOW_ALL_SECTIONS) mIncomingHeaderController else null
+ }
+
+ private val mOnHeadsUpChangedListener = object : OnHeadsUpChangedListener {
+ override fun onHeadsUpStateChanged(entry: NotificationEntry, isHeadsUp: Boolean) {
+ if (!isHeadsUp) {
+ mHeadsUpViewBinder.unbindHeadsUpView(entry)
+ endNotifLifetimeExtensionIfExtended(entry)
+ }
+ }
+ }
+
+ private fun isSticky(entry: NotificationEntry) = mHeadsUpManager.isSticky(entry.key)
+
+ private fun isCurrentlyShowingHun(entry: ListEntry) = mHeadsUpManager.isAlerting(entry.key)
+
+ private fun endNotifLifetimeExtensionIfExtended(entry: NotificationEntry) {
+ if (mNotifsExtendingLifetime.remove(entry)) {
+ mEndLifetimeExtension?.onEndLifetimeExtension(mLifetimeExtender, entry)
+ }
+ }
+
+ companion object {
+ private const val TAG = "HeadsUpCoordinator"
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java
index 33005b34ff98..733be9c1ca2c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java
@@ -36,6 +36,8 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.notification.SectionHeaderVisibilityProvider;
import com.android.systemui.statusbar.notification.collection.GroupEntry;
import com.android.systemui.statusbar.notification.collection.ListEntry;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
@@ -48,7 +50,8 @@ import com.android.systemui.statusbar.policy.KeyguardStateController;
import javax.inject.Inject;
/**
- * Filters low priority and privacy-sensitive notifications from the lockscreen.
+ * Filters low priority and privacy-sensitive notifications from the lockscreen, and hides section
+ * headers on the lockscreen.
*/
@CoordinatorScope
public class KeyguardCoordinator implements Coordinator {
@@ -62,6 +65,7 @@ public class KeyguardCoordinator implements Coordinator {
private final StatusBarStateController mStatusBarStateController;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final HighPriorityProvider mHighPriorityProvider;
+ private final SectionHeaderVisibilityProvider mSectionHeaderVisibilityProvider;
private boolean mHideSilentNotificationsOnLockscreen;
@@ -74,7 +78,8 @@ public class KeyguardCoordinator implements Coordinator {
BroadcastDispatcher broadcastDispatcher,
StatusBarStateController statusBarStateController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
- HighPriorityProvider highPriorityProvider) {
+ HighPriorityProvider highPriorityProvider,
+ SectionHeaderVisibilityProvider sectionHeaderVisibilityProvider) {
mContext = context;
mMainHandler = mainThreadHandler;
mKeyguardStateController = keyguardStateController;
@@ -83,6 +88,7 @@ public class KeyguardCoordinator implements Coordinator {
mStatusBarStateController = statusBarStateController;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mHighPriorityProvider = highPriorityProvider;
+ mSectionHeaderVisibilityProvider = sectionHeaderVisibilityProvider;
}
@Override
@@ -214,6 +220,8 @@ public class KeyguardCoordinator implements Coordinator {
}
private void invalidateListFromFilter(String reason) {
+ mSectionHeaderVisibilityProvider.setSectionHeadersVisible(
+ mStatusBarStateController.getState() != StatusBarState.KEYGUARD);
mNotifFilter.invalidateList();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
index 757fb5a2fe9a..850cb4b88154 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
@@ -20,6 +20,7 @@ import com.android.systemui.dump.DumpManager
import com.android.systemui.statusbar.notification.NotifPipelineFlags
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifComparator
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner
import java.io.FileDescriptor
import java.io.PrintWriter
@@ -63,6 +64,7 @@ class NotifCoordinatorsImpl @Inject constructor(
private val mCoordinators: MutableList<Coordinator> = ArrayList()
private val mOrderedSections: MutableList<NotifSectioner> = ArrayList()
+ private val mOrderedComparators: MutableList<NotifComparator> = ArrayList()
/**
* Creates all the coordinators.
@@ -117,6 +119,9 @@ class NotifCoordinatorsImpl @Inject constructor(
mOrderedSections.add(rankingCoordinator.alertingSectioner) // Alerting
mOrderedSections.add(rankingCoordinator.silentSectioner) // Silent
mOrderedSections.add(rankingCoordinator.minimizedSectioner) // Minimized
+
+ // Manually add ordered comparators
+ mOrderedComparators.add(conversationCoordinator.comparator)
}
/**
@@ -128,6 +133,7 @@ class NotifCoordinatorsImpl @Inject constructor(
c.attach(pipeline)
}
pipeline.setSections(mOrderedSections)
+ pipeline.setComparators(mOrderedComparators)
}
override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<String>) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
index 195f3672dc56..35fe0ee7cdb1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
@@ -31,13 +31,13 @@ import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.IStatusBarService;
-import com.android.systemui.statusbar.notification.ConversationNotificationManager;
import com.android.systemui.statusbar.notification.collection.GroupEntry;
import com.android.systemui.statusbar.notification.collection.ListEntry;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.ShadeListBuilder;
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope;
+import com.android.systemui.statusbar.notification.collection.inflation.BindEventManagerImpl;
import com.android.systemui.statusbar.notification.collection.inflation.NotifInflater;
import com.android.systemui.statusbar.notification.collection.inflation.NotifUiAdjustment;
import com.android.systemui.statusbar.notification.collection.inflation.NotifUiAdjustmentProvider;
@@ -99,7 +99,7 @@ public class PreparationCoordinator implements Coordinator {
/** How long we can delay a group while waiting for all children to inflate */
private final long mMaxGroupInflationDelay;
- private final ConversationNotificationManager mConversationManager;
+ private final BindEventManagerImpl mBindEventManager;
@Inject
public PreparationCoordinator(
@@ -109,7 +109,7 @@ public class PreparationCoordinator implements Coordinator {
NotifViewBarn viewBarn,
NotifUiAdjustmentProvider adjustmentProvider,
IStatusBarService service,
- ConversationNotificationManager conversationManager) {
+ BindEventManagerImpl bindEventManager) {
this(
logger,
notifInflater,
@@ -117,7 +117,7 @@ public class PreparationCoordinator implements Coordinator {
viewBarn,
adjustmentProvider,
service,
- conversationManager,
+ bindEventManager,
CHILD_BIND_CUTOFF,
MAX_GROUP_INFLATION_DELAY);
}
@@ -130,7 +130,7 @@ public class PreparationCoordinator implements Coordinator {
NotifViewBarn viewBarn,
NotifUiAdjustmentProvider adjustmentProvider,
IStatusBarService service,
- ConversationNotificationManager conversationManager,
+ BindEventManagerImpl bindEventManager,
int childBindCutoff,
long maxGroupInflationDelay) {
mLogger = logger;
@@ -141,7 +141,7 @@ public class PreparationCoordinator implements Coordinator {
mStatusBarService = service;
mChildBindCutoff = childBindCutoff;
mMaxGroupInflationDelay = maxGroupInflationDelay;
- mConversationManager = conversationManager;
+ mBindEventManager = bindEventManager;
}
@Override
@@ -369,9 +369,7 @@ public class PreparationCoordinator implements Coordinator {
mInflatingNotifs.remove(entry);
mViewBarn.registerViewForEntry(entry, controller);
mInflationStates.put(entry, STATE_INFLATED);
- // NOTE: under the new pipeline there's no way to register for an inflation callback,
- // so this one method is called by the PreparationCoordinator directly.
- mConversationManager.onEntryViewBound(entry);
+ mBindEventManager.notifyViewBound(entry);
mNotifInflatingFilter.invalidateList();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/BindEventManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/BindEventManager.kt
new file mode 100644
index 000000000000..51bdd00d09be
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/BindEventManager.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.inflation
+
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.util.ListenerSet
+
+/**
+ * Helper class that allows distributing bind events regardless of the pipeline.
+ *
+ * NOTE: This class isn't ideal; this exposes the concept of view inflation as something that can be
+ * globally registered for. This is built as it is to provide compatibility with patterns developed
+ * for the legacy pipeline. Ideally we'd have functionality that needs to know this information be
+ * handled by events that go through the ViewController itself.
+ */
+open class BindEventManager {
+ protected val listeners = ListenerSet<Listener>()
+
+ /** Register a listener */
+ fun addListener(listener: Listener) =
+ listeners.addIfAbsent(listener)
+
+ /** Deregister a listener */
+ fun removeListener(listener: Listener) =
+ listeners.remove(listener)
+
+ /** Listener interface for view bind events */
+ fun interface Listener {
+ fun onViewBound(entry: NotificationEntry)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/BindEventManagerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/BindEventManagerImpl.kt
new file mode 100644
index 000000000000..9d5b859ef29c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/BindEventManagerImpl.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.inflation
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.notification.NotificationEntryListener
+import com.android.systemui.statusbar.notification.NotificationEntryManager
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.inflation.BindEventManager.Listener
+import javax.inject.Inject
+
+/**
+ * Helper class that allows distributing bind events regardless of the pipeline.
+ */
+@SysUISingleton
+class BindEventManagerImpl @Inject constructor() : BindEventManager() {
+ /** Emit the [Listener.onViewBound] event to all registered listeners. */
+ fun notifyViewBound(entry: NotificationEntry) =
+ listeners.forEach { listener -> listener.onViewBound(entry) }
+
+ /** Initialize this for the legacy pipeline. */
+ fun attachToLegacyPipeline(notificationEntryManager: NotificationEntryManager) {
+ notificationEntryManager.addNotificationEntryListener(object : NotificationEntryListener {
+ override fun onEntryInflated(entry: NotificationEntry) = notifyViewBound(entry)
+ override fun onEntryReinflated(entry: NotificationEntry) = notifyViewBound(entry)
+ })
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifComparator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifComparator.java
index 0d150edee128..f7bbd281ec51 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifComparator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifComparator.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.notification.collection.listbuilder.pluggable;
+import androidx.annotation.NonNull;
+
import com.android.systemui.statusbar.notification.collection.ListEntry;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
@@ -39,5 +41,5 @@ public abstract class NotifComparator
* @return a negative integer, zero, or a positive integer as the first argument is less than
* equal to, or greater than the second (same as standard Comparator<> interface).
*/
- public abstract int compare(ListEntry o1, ListEntry o2);
+ public abstract int compare(@NonNull ListEntry o1, @NonNull ListEntry o2);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionListener.java
index 68a346f817e1..9d56a8ede1cc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionListener.java
@@ -17,6 +17,8 @@
package com.android.systemui.statusbar.notification.collection.notifcollection;
import android.annotation.NonNull;
+import android.app.NotificationChannel;
+import android.os.UserHandle;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
@@ -115,4 +117,20 @@ public interface NotifCollectionListener {
*/
default void onRankingUpdate(NotificationListenerService.RankingMap rankingMap) {
}
+
+ /**
+ * Called when a notification channel is modified, in response to
+ * {@link NotificationListenerService#onNotificationChannelModified}.
+ *
+ * @param pkgName the package the notification channel belongs to.
+ * @param user the user the notification channel belongs to.
+ * @param channel the channel being modified.
+ * @param modificationType the type of modification that occurred to the channel.
+ */
+ default void onNotificationChannelModified(
+ String pkgName,
+ UserHandle user,
+ NotificationChannel channel,
+ int modificationType) {
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifEvent.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifEvent.kt
index 179e95328442..e20f0e50af6d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifEvent.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifEvent.kt
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.notification.collection.notifcollection
+import android.app.NotificationChannel
+import android.os.UserHandle
import android.service.notification.NotificationListenerService.RankingMap
import android.service.notification.StatusBarNotification
import com.android.systemui.statusbar.notification.collection.NotifCollection
@@ -102,3 +104,14 @@ class RankingAppliedEvent() : NotifEvent() {
listener.onRankingApplied()
}
}
+
+data class ChannelChangedEvent(
+ val pkgName: String,
+ val user: UserHandle,
+ val channel: NotificationChannel,
+ val modificationType: Int
+) : NotifEvent() {
+ override fun dispatchToListener(listener: NotifCollectionListener) {
+ listener.onNotificationChannelModified(pkgName, user, channel, modificationType)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/DebugModeFilterProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/DebugModeFilterProvider.kt
index d16d76ad2f9a..ab777de21e34 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/DebugModeFilterProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/DebugModeFilterProvider.kt
@@ -77,7 +77,7 @@ class DebugModeFilterProvider @Inject constructor(
if (needsInitialization) {
val filter = IntentFilter().apply { addAction(ACTION_SET_NOTIF_DEBUG_MODE) }
val permission = NOTIF_DEBUG_MODE_PERMISSION
- context.registerReceiver(mReceiver, filter, permission, null)
+ context.registerReceiver(mReceiver, filter, permission, null, Context.RECEIVER_EXPORTED)
Log.d(TAG, "Registered: $mReceiver")
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeController.kt
index 289dacbca69e..26ba12c97575 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeController.kt
@@ -41,17 +41,29 @@ interface NodeController {
fun getChildCount(): Int = 0
+ /** Called to add a child to this view */
fun addChildAt(child: NodeController, index: Int) {
throw RuntimeException("Not supported")
}
+ /** Called to move one of this view's current children to a new position */
fun moveChildTo(child: NodeController, index: Int) {
throw RuntimeException("Not supported")
}
+ /** Called to remove one of this view's current children */
fun removeChild(child: NodeController, isTransfer: Boolean) {
throw RuntimeException("Not supported")
}
+
+ /** Called when this view has been added */
+ fun onViewAdded() {}
+
+ /** Called when this view has been moved */
+ fun onViewMoved() {}
+
+ /** Called when this view has been removed */
+ fun onViewRemoved() {}
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt
index f13470ec2c94..607500edfd3a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.collection.render
+import com.android.systemui.statusbar.notification.SectionHeaderVisibilityProvider
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
import com.android.systemui.statusbar.notification.collection.GroupEntry
import com.android.systemui.statusbar.notification.collection.ListEntry
@@ -35,6 +36,7 @@ import com.android.systemui.util.traceSection
class NodeSpecBuilder(
private val mediaContainerController: MediaContainerController,
private val sectionsFeatureManager: NotificationSectionsFeatureManager,
+ private val sectionHeaderVisibilityProvider: SectionHeaderVisibilityProvider,
private val viewBarn: NotifViewBarn
) {
fun buildNodeSpec(
@@ -51,6 +53,7 @@ class NodeSpecBuilder(
var currentSection: NotifSection? = null
val prevSections = mutableSetOf<NotifSection?>()
+ val showHeaders = sectionHeaderVisibilityProvider.sectionHeadersVisible
for (entry in notifList) {
val section = entry.section!!
@@ -61,7 +64,7 @@ class NodeSpecBuilder(
// If this notif begins a new section, first add the section's header view
if (section != currentSection) {
- if (section.headerController != currentSection?.headerController) {
+ if (section.headerController != currentSection?.headerController && showHeaders) {
section.headerController?.let { headerController ->
root.children.add(NodeSpecImpl(root, headerController))
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RootNodeController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RootNodeController.kt
index a1800ed12125..4de8e7a5641c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RootNodeController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RootNodeController.kt
@@ -40,6 +40,7 @@ class RootNodeController(
override fun addChildAt(child: NodeController, index: Int) {
listContainer.addContainerViewAt(child.view, index)
+ listContainer.onNotificationViewUpdateFinished()
}
override fun moveChildTo(child: NodeController, index: Int) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/SectionHeaderController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/SectionHeaderController.kt
index 4e9017e05ecd..2c9508e84aef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/SectionHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/SectionHeaderController.kt
@@ -94,6 +94,10 @@ internal class SectionHeaderNodeControllerImpl @Inject constructor(
_view?.setOnClearAllClickListener(listener)
}
+ override fun onViewAdded() {
+ headerView?.isContentVisible = true
+ }
+
override val view: View
get() = _view!!
} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt
index 6d4ae4b1a869..28cd28594c3e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt
@@ -215,13 +215,16 @@ private class ShadeNode(
fun addChildAt(child: ShadeNode, index: Int) {
controller.addChildAt(child.controller, index)
+ child.controller.onViewAdded()
}
fun moveChildTo(child: ShadeNode, index: Int) {
controller.moveChildTo(child.controller, index)
+ child.controller.onViewMoved()
}
fun removeChild(child: ShadeNode, isTransfer: Boolean) {
controller.removeChild(child.controller, isTransfer)
+ child.controller.onViewRemoved()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
index ad973927f21e..484707241b4d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
@@ -19,6 +19,7 @@ package com.android.systemui.statusbar.notification.collection.render
import android.content.Context
import android.view.View
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
+import com.android.systemui.statusbar.notification.SectionHeaderVisibilityProvider
import com.android.systemui.statusbar.notification.collection.GroupEntry
import com.android.systemui.statusbar.notification.collection.ListEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntry
@@ -38,13 +39,15 @@ class ShadeViewManager @AssistedInject constructor(
@Assisted private val stackController: NotifStackController,
mediaContainerController: MediaContainerController,
featureManager: NotificationSectionsFeatureManager,
+ sectionHeaderVisibilityProvider: SectionHeaderVisibilityProvider,
logger: ShadeViewDifferLogger,
private val viewBarn: NotifViewBarn
) {
// We pass a shim view here because the listContainer may not actually have a view associated
// with it and the differ never actually cares about the root node's view.
private val rootController = RootNodeController(listContainer, View(context))
- private val specBuilder = NodeSpecBuilder(mediaContainerController, featureManager, viewBarn)
+ private val specBuilder = NodeSpecBuilder(mediaContainerController, featureManager,
+ sectionHeaderVisibilityProvider, viewBarn)
private val viewDiffer = ShadeViewDiffer(rootController, logger)
/** Method for attaching this manager to the pipeline. */
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 f1cba34158d1..05c40b204c86 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
@@ -50,6 +50,8 @@ import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.coordinator.ShadeEventCoordinator;
import com.android.systemui.statusbar.notification.collection.coordinator.VisualStabilityCoordinator;
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorsModule;
+import com.android.systemui.statusbar.notification.collection.inflation.BindEventManager;
+import com.android.systemui.statusbar.notification.collection.inflation.BindEventManagerImpl;
import com.android.systemui.statusbar.notification.collection.inflation.NotifInflater;
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
import com.android.systemui.statusbar.notification.collection.inflation.OnUserInteractionCallbackImpl;
@@ -358,5 +360,9 @@ public interface NotificationsModule {
/** */
@Binds
+ BindEventManager bindBindEventManagerImpl(BindEventManagerImpl bindEventManagerImpl);
+
+ /** */
+ @Binds
NotifLiveDataStore bindNotifLiveDataStore(NotifLiveDataStoreImpl notifLiveDataStoreImpl);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
index 38f3c39b5b1a..48f2dafedcbb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
@@ -32,6 +32,7 @@ import com.android.systemui.statusbar.notification.collection.NotifLiveDataStore
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationRankingManager
import com.android.systemui.statusbar.notification.collection.TargetSdkResolver
+import com.android.systemui.statusbar.notification.collection.inflation.BindEventManagerImpl
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl
import com.android.systemui.statusbar.notification.collection.init.NotifPipelineInitializer
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy
@@ -76,6 +77,7 @@ class NotificationsControllerImpl @Inject constructor(
private val notifBindPipelineInitializer: NotifBindPipelineInitializer,
private val deviceProvisionedController: DeviceProvisionedController,
private val notificationRowBinder: NotificationRowBinderImpl,
+ private val bindEventManagerImpl: BindEventManagerImpl,
private val remoteInputUriController: RemoteInputUriController,
private val groupManagerLegacy: Lazy<NotificationGroupManagerLegacy>,
private val groupAlertTransferHelper: NotificationGroupAlertTransferHelper,
@@ -131,6 +133,7 @@ class NotificationsControllerImpl @Inject constructor(
targetSdkResolver.initialize(entryManager)
remoteInputUriController.attach(entryManager)
groupAlertTransferHelper.bind(entryManager, groupManagerLegacy.get())
+ bindEventManagerImpl.attachToLegacyPipeline(entryManager)
headsUpManager.addListener(groupManagerLegacy.get())
headsUpManager.addListener(groupAlertTransferHelper)
headsUpController.attach(entryManager, headsUpManager)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
index b28fb58967bd..46efef66de43 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
@@ -268,6 +268,18 @@ public class ExpandableNotificationRowController implements NotifViewController
}
@Override
+ public void onViewAdded() {
+ }
+
+ @Override
+ public void onViewMoved() {
+ }
+
+ @Override
+ public void onViewRemoved() {
+ }
+
+ @Override
public int getChildCount() {
final List<ExpandableNotificationRow> mChildren = mView.getAttachedChildren();
return mChildren != null ? mChildren.size() : 0;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
index 624e7416d3ee..6eff7993c59c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
@@ -552,14 +552,8 @@ public abstract class ExpandableView extends FrameLayout implements Dumpable {
final ViewGroup transientContainer = getTransientContainer();
if (parent == null || parent == newParent) {
// If this view's current parent is null or the same as the new parent, the add will
- // succeed, so just make sure the tracked transient container is in sync with the
- // current parent.
- if (transientContainer != null && transientContainer != parent) {
- Log.w(TAG, "Expandable view " + this
- + " has transient container " + transientContainer
- + " but different parent" + parent);
- setTransientContainer(null);
- }
+ // succeed as long as it's a true child, so just make sure the view isn't transient.
+ removeFromTransientContainer();
return;
}
if (transientContainer == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
index 9c755e970a0f..3cdaa9ad5f87 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
@@ -105,6 +105,9 @@ public abstract class StackScrollerDecorView extends ExpandableView {
runAfter.run();
};
setViewVisible(mContent, visible, animate, endRunnable);
+ } else if (runAfter != null) {
+ // Execute the runAfter runnable immediately if there's no animation to perform.
+ runAfter.run();
}
if (!mContentAnimating) {
@@ -228,7 +231,7 @@ public abstract class StackScrollerDecorView extends ExpandableView {
Runnable onFinishedRunnable,
AnimatorListenerAdapter animationListener) {
// TODO: Use duration
- setContentVisible(false);
+ setContentVisible(false, true /* animate */, onFinishedRunnable);
return 0;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index dee1b334182a..8d500fa4e8b0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -44,6 +44,7 @@ import com.android.systemui.biometrics.AuthController;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.WakefulnessLifecycle;
@@ -254,6 +255,8 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
);
}
+ private KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
+
@Inject
public BiometricUnlockController(Context context, DozeScrimController dozeScrimController,
KeyguardViewMediator keyguardViewMediator, ScrimController scrimController,
@@ -269,7 +272,8 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
WakefulnessLifecycle wakefulnessLifecycle,
ScreenLifecycle screenLifecycle,
AuthController authController,
- StatusBarStateController statusBarStateController) {
+ StatusBarStateController statusBarStateController,
+ KeyguardUnlockAnimationController keyguardUnlockAnimationController) {
mContext = context;
mPowerManager = powerManager;
mShadeController = shadeController;
@@ -292,6 +296,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
mMetricsLogger = metricsLogger;
mAuthController = authController;
mStatusBarStateController = statusBarStateController;
+ mKeyguardUnlockAnimationController = keyguardUnlockAnimationController;
dumpManager.registerDumpable(getClass().getName(), this);
}
@@ -438,11 +443,15 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
if (!wasDeviceInteractive) {
mPendingShowBouncer = true;
} else {
- mShadeController.animateCollapsePanels(
- CommandQueue.FLAG_EXCLUDE_NONE,
- true /* force */,
- false /* delayed */,
- BIOMETRIC_COLLAPSE_SPEEDUP_FACTOR);
+ // If the keyguard unlock controller is going to handle the unlock animation, it
+ // will fling the panel collapsed when it's ready.
+ if (!mKeyguardUnlockAnimationController.willHandleUnlockAnimation()) {
+ mShadeController.animateCollapsePanels(
+ CommandQueue.FLAG_EXCLUDE_NONE,
+ true /* force */,
+ false /* delayed */,
+ BIOMETRIC_COLLAPSE_SPEEDUP_FACTOR);
+ }
mPendingShowBouncer = false;
mKeyguardViewController.notifyKeyguardAuthenticated(
false /* strongAuth */);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
index 1b42b58a55aa..d610b372702d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
@@ -305,6 +305,14 @@ public class DozeParameters implements
}
/**
+ * When this method returns true then moving display state to power save mode will be
+ * delayed for a few seconds. This might be useful to play animations without reducing FPS.
+ */
+ public boolean shouldDelayDisplayDozeTransition() {
+ return mScreenOffAnimationController.shouldDelayDisplayDozeTransition();
+ }
+
+ /**
* Whether we're capable of controlling the screen off animation if we want to. This isn't
* possible if AOD isn't even enabled or if the flag is disabled.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index 5d6e807729fa..aff73e456b2e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -349,6 +349,9 @@ public class NotificationIconAreaController implements
}
private void updateShelfIcons() {
+ if (mShelfIcons == null) {
+ return;
+ }
updateIconsForLayout(entry -> entry.getIcons().getShelfIcon(), mShelfIcons,
true /* showAmbient */,
true /* showLowPriority */,
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 0bc633c74fc7..769f68976958 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -46,6 +46,7 @@ import static java.lang.Float.isNaN;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
+import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.Fragment;
import android.app.StatusBarManager;
@@ -137,6 +138,7 @@ import com.android.systemui.fragments.FragmentService;
import com.android.systemui.idle.IdleHostView;
import com.android.systemui.idle.IdleHostViewController;
import com.android.systemui.idle.dagger.IdleViewComponent;
+import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.media.KeyguardMediaController;
import com.android.systemui.media.MediaDataManager;
import com.android.systemui.media.MediaHierarchyManager;
@@ -211,8 +213,10 @@ import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
import java.util.Optional;
+import java.util.Set;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -788,7 +792,8 @@ public class NotificationPanelViewController extends PanelViewController {
Optional<SysUIUnfoldComponent> unfoldComponent,
ControlsComponent controlsComponent,
InteractionJankMonitor interactionJankMonitor,
- QsFrameTranslateController qsFrameTranslateController) {
+ QsFrameTranslateController qsFrameTranslateController,
+ KeyguardUnlockAnimationController keyguardUnlockAnimationController) {
super(view,
featureFlags,
falsingManager,
@@ -803,7 +808,8 @@ public class NotificationPanelViewController extends PanelViewController {
lockscreenGestureLogger,
panelExpansionStateManager,
ambientState,
- interactionJankMonitor);
+ interactionJankMonitor,
+ keyguardUnlockAnimationController);
mView = view;
mVibratorHelper = vibratorHelper;
mKeyguardMediaController = keyguardMediaController;
@@ -922,8 +928,33 @@ public class NotificationPanelViewController extends PanelViewController {
mQsFrameTranslateController = qsFrameTranslateController;
updateUserSwitcherFlags();
onFinishInflate();
-
mUseCombinedQSHeaders = featureFlags.isEnabled(Flags.COMBINED_QS_HEADERS);
+ keyguardUnlockAnimationController.addKeyguardUnlockAnimationListener(
+ new KeyguardUnlockAnimationController.KeyguardUnlockAnimationListener() {
+ @Override
+ public void onUnlockAnimationFinished() {
+ // Make sure the clock is in the correct position after the unlock animation
+ // so that it's not in the wrong place when we show the keyguard again.
+ positionClockAndNotifications(true /* forceClockUpdate */);
+ }
+
+ @Override
+ public void onUnlockAnimationStarted(
+ boolean playingCannedAnimation, boolean isWakeAndUnlock) {
+ // Disable blurs while we're unlocking so that panel expansion does not
+ // cause blurring. This will eventually be re-enabled by the panel view on
+ // ACTION_UP, since the user's finger might still be down after a swipe to
+ // unlock gesture, and we don't want that to cause blurring either.
+ mDepthController.setBlursDisabledForUnlock(mTracking);
+
+ if (playingCannedAnimation && !isWakeAndUnlock) {
+ // Fling the panel away so it's not in the way or the surface behind the
+ // keyguard, which will be appearing. If we're wake and unlocking, the
+ // lock screen is hidden instantly so should not be flung away.
+ fling(0f, false, 0.7f, false);
+ }
+ }
+ });
}
private void onFinishInflate() {
@@ -3321,6 +3352,10 @@ public class NotificationPanelViewController extends PanelViewController {
mAffordanceHelper.reset(true);
}
}
+
+ // If we unlocked from a swipe, the user's finger might still be down after the
+ // unlock animation ends. We need to wait until ACTION_UP to enable blurs again.
+ mDepthController.setBlursDisabledForUnlock(false);
}
private void updateMaxHeadsUpTranslation() {
@@ -4596,7 +4631,14 @@ public class NotificationPanelViewController extends PanelViewController {
// Can affect multi-user switcher visibility as it depends on screen size by default:
// it is enabled only for devices with large screens (see config_keyguardUserSwitcher)
- reInflateViews();
+ boolean prevKeyguardUserSwitcherEnabled = mKeyguardUserSwitcherEnabled;
+ boolean prevKeyguardQsUserSwitchEnabled = mKeyguardQsUserSwitchEnabled;
+ updateUserSwitcherFlags();
+ if (prevKeyguardUserSwitcherEnabled != mKeyguardUserSwitcherEnabled
+ || prevKeyguardQsUserSwitchEnabled != mKeyguardQsUserSwitchEnabled) {
+ reInflateViews();
+ }
+
Trace.endSection();
}
@@ -4913,33 +4955,53 @@ public class NotificationPanelViewController extends PanelViewController {
private class DebugDrawable extends Drawable {
+ private final Set<Integer> mDebugTextUsedYPositions = new HashSet<>();
+ private final Paint mDebugPaint = new Paint();
+
@Override
- public void draw(Canvas canvas) {
- Paint p = new Paint();
- p.setColor(Color.RED);
- p.setStrokeWidth(2);
- p.setStyle(Paint.Style.STROKE);
- canvas.drawLine(0, getMaxPanelHeight(), mView.getWidth(), getMaxPanelHeight(), p);
- p.setTextSize(24);
- if (mHeaderDebugInfo != null) canvas.drawText(mHeaderDebugInfo, 50, 100, p);
- p.setColor(Color.BLUE);
- canvas.drawLine(0, getExpandedHeight(), mView.getWidth(), getExpandedHeight(), p);
- p.setColor(Color.GREEN);
- canvas.drawLine(0, calculatePanelHeightQsExpanded(), mView.getWidth(),
- calculatePanelHeightQsExpanded(), p);
- p.setColor(Color.YELLOW);
- canvas.drawLine(0, calculatePanelHeightShade(), mView.getWidth(),
- calculatePanelHeightShade(), p);
- p.setColor(Color.MAGENTA);
- canvas.drawLine(
- 0, calculateNotificationsTopPadding(), mView.getWidth(),
- calculateNotificationsTopPadding(), p);
- p.setColor(Color.CYAN);
+ public void draw(@NonNull Canvas canvas) {
+ mDebugTextUsedYPositions.clear();
+
+ mDebugPaint.setColor(Color.RED);
+ mDebugPaint.setStrokeWidth(2);
+ mDebugPaint.setStyle(Paint.Style.STROKE);
+ mDebugPaint.setTextSize(24);
+ if (mHeaderDebugInfo != null) canvas.drawText(mHeaderDebugInfo, 50, 100, mDebugPaint);
+
+ drawDebugInfo(canvas, getMaxPanelHeight(), Color.RED, "getMaxPanelHeight()");
+ drawDebugInfo(canvas, (int) getExpandedHeight(), Color.BLUE, "getExpandedHeight()");
+ drawDebugInfo(canvas, calculatePanelHeightQsExpanded(), Color.GREEN,
+ "calculatePanelHeightQsExpanded()");
+ drawDebugInfo(canvas, calculatePanelHeightShade(), Color.YELLOW,
+ "calculatePanelHeightShade()");
+ drawDebugInfo(canvas, (int) calculateNotificationsTopPadding(), Color.MAGENTA,
+ "calculateNotificationsTopPadding()");
+ drawDebugInfo(canvas, mClockPositionResult.clockY, Color.GRAY,
+ "mClockPositionResult.clockY");
+
+ mDebugPaint.setColor(Color.CYAN);
canvas.drawLine(0, mClockPositionResult.stackScrollerPadding, mView.getWidth(),
- mNotificationStackScrollLayoutController.getTopPadding(), p);
- p.setColor(Color.GRAY);
- canvas.drawLine(0, mClockPositionResult.clockY, mView.getWidth(),
- mClockPositionResult.clockY, p);
+ mNotificationStackScrollLayoutController.getTopPadding(), mDebugPaint);
+ }
+
+ private void drawDebugInfo(Canvas canvas, int y, int color, String label) {
+ mDebugPaint.setColor(color);
+ canvas.drawLine(/* startX= */ 0, /* startY= */ y, /* stopX= */ mView.getWidth(),
+ /* stopY= */ y, mDebugPaint);
+ canvas.drawText(label, /* x= */ 0, /* y= */ computeDebugYTextPosition(y), mDebugPaint);
+ }
+
+ private int computeDebugYTextPosition(int lineY) {
+ if (lineY - mDebugPaint.getTextSize() < 0) {
+ // Avoiding drawing out of bounds
+ lineY += mDebugPaint.getTextSize();
+ }
+ int textY = lineY;
+ while (mDebugTextUsedYPositions.contains(textY)) {
+ textY = (int) (textY + mDebugPaint.getTextSize());
+ }
+ mDebugTextUsedYPositions.add(textY);
+ return textY;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
index 53bfd7701335..05ac2a35c777 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
@@ -55,6 +55,7 @@ import com.android.systemui.classifier.Classifier;
import com.android.systemui.doze.DozeLog;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
+import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
@@ -212,6 +213,8 @@ public abstract class PanelViewController {
return mAmbientState;
}
+ private KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
+
public PanelViewController(
PanelView view,
FeatureFlags featureFlags,
@@ -227,7 +230,15 @@ public abstract class PanelViewController {
LockscreenGestureLogger lockscreenGestureLogger,
PanelExpansionStateManager panelExpansionStateManager,
AmbientState ambientState,
- InteractionJankMonitor interactionJankMonitor) {
+ InteractionJankMonitor interactionJankMonitor,
+ KeyguardUnlockAnimationController keyguardUnlockAnimationController) {
+ mKeyguardUnlockAnimationController = keyguardUnlockAnimationController;
+ keyguardStateController.addCallback(new KeyguardStateController.Callback() {
+ @Override
+ public void onKeyguardFadingAwayChanged() {
+ requestPanelHeightUpdate();
+ }
+ });
mAmbientState = ambientState;
mView = view;
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
@@ -437,7 +448,8 @@ public abstract class PanelViewController {
mUpdateFlingVelocity = vel;
}
} else if (!mStatusBar.isBouncerShowing()
- && !mStatusBarKeyguardViewManager.isShowingAlternateAuthOrAnimating()) {
+ && !mStatusBarKeyguardViewManager.isShowingAlternateAuthOrAnimating()
+ && !mKeyguardStateController.isKeyguardGoingAway()) {
boolean expands = onEmptySpaceClick(mInitialTouchX);
onTrackingStopped(expands);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenOffAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenOffAnimationController.kt
index e806ca0d9005..091831f36022 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenOffAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenOffAnimationController.kt
@@ -180,6 +180,14 @@ class ScreenOffAnimationController @Inject constructor(
animations.all { it.shouldAnimateDozingChange() }
/**
+ * Returns true when moving display state to power save mode should be
+ * delayed for a few seconds. This might be useful to play animations in full quality,
+ * without reducing FPS.
+ */
+ fun shouldDelayDisplayDozeTransition(): Boolean =
+ animations.any { it.shouldDelayDisplayDozeTransition() }
+
+ /**
* Return true to animate large <-> small clock transition
*/
fun shouldAnimateClockChange(): Boolean =
@@ -207,6 +215,7 @@ interface ScreenOffAnimation {
fun shouldHideScrimOnWakeUp(): Boolean = false
fun overrideNotificationsDozeAmount(): Boolean = false
fun shouldShowAodIconsWhenShade(): Boolean = false
+ fun shouldDelayDisplayDozeTransition(): Boolean = false
fun shouldAnimateAodIcons(): Boolean = true
fun shouldAnimateDozingChange(): Boolean = true
fun shouldAnimateClockChange(): Boolean = true
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 a23e726e0b6b..48048b49e5b8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -182,6 +182,9 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
private GradientColors mColors;
private boolean mNeedsDrawableColorUpdate;
+ private float mAdditionalScrimBehindAlphaKeyguard = 0f;
+ // Combined scrim behind keyguard alpha of default scrim + additional scrim
+ // (if wallpaper dimming is applied).
private float mScrimBehindAlphaKeyguard = KEYGUARD_SCRIM_ALPHA;
private final float mDefaultScrimAlpha;
@@ -437,7 +440,35 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
return mState;
}
- protected void setScrimBehindValues(float scrimBehindAlphaKeyguard) {
+ /**
+ * Sets the additional scrim behind alpha keyguard that would be blended with the default scrim
+ * by applying alpha composition on both values.
+ *
+ * @param additionalScrimAlpha alpha value of additional scrim behind alpha keyguard.
+ */
+ protected void setAdditionalScrimBehindAlphaKeyguard(float additionalScrimAlpha) {
+ mAdditionalScrimBehindAlphaKeyguard = additionalScrimAlpha;
+ }
+
+ /**
+ * Applies alpha composition to the default scrim behind alpha keyguard and the additional
+ * scrim alpha, and sets this value to the scrim behind alpha keyguard.
+ * This is used to apply additional keyguard dimming on top of the default scrim alpha value.
+ */
+ protected void applyCompositeAlphaOnScrimBehindKeyguard() {
+ int compositeAlpha = ColorUtils.compositeAlpha(
+ (int) (255 * mAdditionalScrimBehindAlphaKeyguard),
+ (int) (255 * KEYGUARD_SCRIM_ALPHA));
+ float keyguardScrimAlpha = (float) compositeAlpha / 255;
+ setScrimBehindValues(keyguardScrimAlpha);
+ }
+
+ /**
+ * Sets the scrim behind alpha keyguard values. This is how much the keyguard will be dimmed.
+ *
+ * @param scrimBehindAlphaKeyguard alpha value of the scrim behind
+ */
+ private void setScrimBehindValues(float scrimBehindAlphaKeyguard) {
mScrimBehindAlphaKeyguard = scrimBehindAlphaKeyguard;
ScrimState[] states = ScrimState.values();
for (int i = 0; i < states.length; i++) {
@@ -676,7 +707,10 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
mNotificationsAlpha = behindFraction * mDefaultScrimAlpha;
} else {
mBehindAlpha = behindFraction * mDefaultScrimAlpha;
- mNotificationsAlpha = mBehindAlpha;
+ // Delay fade-in of notification scrim a bit further, to coincide with the
+ // view fade in. Otherwise the empty panel can be quite jarring.
+ mNotificationsAlpha = MathUtils.constrainedMap(0f, 1f, 0.3f, 0.75f,
+ mPanelExpansionFraction);
}
mInFrontAlpha = 0;
}
@@ -732,7 +766,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
}
if (mUnOcclusionAnimationRunning && mState == ScrimState.KEYGUARD) {
// We're unoccluding the keyguard and don't want to have a bright flash.
- mNotificationsAlpha = KEYGUARD_SCRIM_ALPHA;
+ mNotificationsAlpha = mScrimBehindAlphaKeyguard;
mNotificationsTint = ScrimState.KEYGUARD.getNotifTint();
}
}
@@ -775,6 +809,12 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
: ScrimState.SHADE_LOCKED.getBehindTint();
behindTint = ColorUtils.blendARGB(behindTint, stateTint, mQsExpansion);
}
+
+ // If the keyguard is going away, we should not be opaque.
+ if (mKeyguardStateController.isKeyguardGoingAway()) {
+ behindAlpha = 0f;
+ }
+
return new Pair<>(behindTint, behindAlpha);
}
@@ -1299,9 +1339,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
public void setExpansionAffectsAlpha(boolean expansionAffectsAlpha) {
mExpansionAffectsAlpha = expansionAffectsAlpha;
- if (expansionAffectsAlpha) {
- applyAndDispatchState();
- }
}
public void setKeyguardOccluded(boolean keyguardOccluded) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
index 8afa63719564..d2e1650056ac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -153,7 +153,7 @@ public enum ScrimState {
// to make sure correct color is returned before "prepare" is called
@Override
public int getBehindTint() {
- return Color.BLACK;
+ return DEBUG_MODE ? DEBUG_BEHIND_TINT : Color.BLACK;
}
},
@@ -264,6 +264,12 @@ public enum ScrimState {
}
};
+ private static final boolean DEBUG_MODE = false;
+
+ private static final int DEBUG_NOTIFICATIONS_TINT = Color.RED;
+ private static final int DEBUG_FRONT_TINT = Color.GREEN;
+ private static final int DEBUG_BEHIND_TINT = Color.BLUE;
+
boolean mBlankScreen = false;
long mAnimationDuration = ScrimController.ANIMATION_DURATION;
int mFrontTint = Color.TRANSPARENT;
@@ -323,15 +329,15 @@ public enum ScrimState {
}
public int getFrontTint() {
- return mFrontTint;
+ return DEBUG_MODE ? DEBUG_FRONT_TINT : mFrontTint;
}
public int getBehindTint() {
- return mBehindTint;
+ return DEBUG_MODE ? DEBUG_BEHIND_TINT : mBehindTint;
}
public int getNotifTint() {
- return mNotifTint;
+ return DEBUG_MODE ? DEBUG_NOTIFICATIONS_TINT : mNotifTint;
}
public long getAnimationDuration() {
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 f8b0535b7ec7..72237b1ca6c6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java
@@ -123,7 +123,8 @@ public class ShadeControllerImpl implements ShadeController {
+ " canPanelBeCollapsed(): "
+ getNotificationPanelViewController().canPanelBeCollapsed());
if (getNotificationShadeWindowView() != null
- && getNotificationPanelViewController().canPanelBeCollapsed()) {
+ && getNotificationPanelViewController().canPanelBeCollapsed()
+ && (flags & CommandQueue.FLAG_EXCLUDE_NOTIFICATION_PANEL) == 0) {
// release focus immediately to kick off focus change transition
mNotificationShadeWindowController.setNotificationShadeFocusable(false);
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 649aa42f693b..ae4a19e2b212 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -268,6 +268,7 @@ public class StatusBar extends CoreStartable implements
// Should match the values in PhoneWindowManager
public static final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps";
+ public static final String SYSTEM_DIALOG_REASON_DREAM = "dream";
static public final String SYSTEM_DIALOG_REASON_SCREENSHOT = "screenshot";
private static final String BANNER_ACTION_CANCEL =
@@ -618,6 +619,8 @@ public class StatusBar extends CoreStartable implements
protected boolean mDozing;
private boolean mIsFullscreen;
+ boolean mCloseQsBeforeScreenOff;
+
private final NotificationMediaManager mMediaManager;
private final NotificationLockscreenUserManager mLockscreenUserManager;
private final NotificationRemoteInputManager mRemoteInputManager;
@@ -1123,6 +1126,15 @@ public class StatusBar extends CoreStartable implements
}
if (leaveOpen) {
mStatusBarStateController.setLeaveOpenOnKeyguardHide(true);
+ if (mIsKeyguard) {
+ // When device state changes on keyguard we don't want to keep the state of
+ // the shade and instead we open clean state of keyguard with shade closed.
+ // Normally some parts of QS state (like expanded/collapsed) are persisted and
+ // that causes incorrect UI rendering, especially when changing state with QS
+ // expanded. To prevent that we can close QS which resets QS and some parts of
+ // the shade to its default state. Read more in b/201537421
+ mCloseQsBeforeScreenOff = true;
+ }
}
}
@@ -2635,8 +2647,17 @@ public class StatusBar extends CoreStartable implements
if (mLockscreenUserManager.isCurrentProfile(getSendingUserId())) {
int flags = CommandQueue.FLAG_EXCLUDE_NONE;
String reason = intent.getStringExtra("reason");
- if (reason != null && reason.equals(SYSTEM_DIALOG_REASON_RECENT_APPS)) {
- flags |= CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL;
+ if (reason != null) {
+ if (reason.equals(SYSTEM_DIALOG_REASON_RECENT_APPS)) {
+ flags |= CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL;
+ }
+ // Do not collapse notifications when starting dreaming if the notifications
+ // shade is used for the screen off animation. It might require expanded
+ // state for the scrims to be visible
+ if (reason.equals(SYSTEM_DIALOG_REASON_DREAM)
+ && mScreenOffAnimationController.shouldExpandNotifications()) {
+ flags |= CommandQueue.FLAG_EXCLUDE_NOTIFICATION_PANEL;
+ }
}
mShadeController.animateCollapsePanels(flags);
}
@@ -2913,10 +2934,10 @@ public class StatusBar extends CoreStartable implements
}
boolean updateIsKeyguard() {
- return updateIsKeyguard(false /* force */);
+ return updateIsKeyguard(false /* forceStateChange */);
}
- boolean updateIsKeyguard(boolean force) {
+ boolean updateIsKeyguard(boolean forceStateChange) {
boolean wakeAndUnlocking = mBiometricUnlockController.getMode()
== BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
@@ -2949,7 +2970,7 @@ public class StatusBar extends CoreStartable implements
// as the animation could prepare 'fake AOD' interface (without actually
// transitioning to keyguard state) and this might reset the view states
if (!mScreenOffAnimationController.isKeyguardHideDelayed()) {
- return hideKeyguardImpl(force);
+ return hideKeyguardImpl(forceStateChange);
}
}
return false;
@@ -2957,6 +2978,8 @@ public class StatusBar extends CoreStartable implements
public void showKeyguardImpl() {
mIsKeyguard = true;
+ // In case we're locking while a smartspace transition is in progress, reset it.
+ mKeyguardUnlockAnimationController.resetSmartspaceTransition();
if (mKeyguardStateController.isLaunchTransitionFadingAway()) {
mNotificationPanelViewController.cancelAnimation();
onLaunchTransitionFadingEnded();
@@ -3077,12 +3100,12 @@ public class StatusBar extends CoreStartable implements
/**
* @return true if we would like to stay in the shade, false if it should go away entirely
*/
- public boolean hideKeyguardImpl(boolean force) {
+ public boolean hideKeyguardImpl(boolean forceStateChange) {
mIsKeyguard = false;
Trace.beginSection("StatusBar#hideKeyguard");
boolean staying = mStatusBarStateController.leaveOpenOnKeyguardHide();
int previousState = mStatusBarStateController.getState();
- if (!(mStatusBarStateController.setState(StatusBarState.SHADE, force))) {
+ if (!(mStatusBarStateController.setState(StatusBarState.SHADE, forceStateChange))) {
//TODO: StatusBarStateController should probably know about hiding the keyguard and
// notify listeners.
@@ -3135,6 +3158,7 @@ public class StatusBar extends CoreStartable implements
// bar.
mKeyguardStateController.notifyKeyguardGoingAway(true);
mCommandQueue.appTransitionPending(mDisplayId, true /* forced */);
+ updateScrimController();
}
/**
@@ -3168,16 +3192,29 @@ public class StatusBar extends CoreStartable implements
* Switches theme from light to dark and vice-versa.
*/
protected void updateTheme() {
+ // Set additional scrim only if the lock and system wallpaper are different to prevent
+ // applying the dimming effect twice.
+ mUiBgExecutor.execute(() -> {
+ float dimAmount = 0f;
+ if (mWallpaperManager.lockScreenWallpaperExists()) {
+ dimAmount = mWallpaperManager.getWallpaperDimAmount();
+ }
+ final float scrimDimAmount = dimAmount;
+ mMainExecutor.execute(() -> {
+ mScrimController.setAdditionalScrimBehindAlphaKeyguard(scrimDimAmount);
+ mScrimController.applyCompositeAlphaOnScrimBehindKeyguard();
+ });
+ });
+
// Lock wallpaper defines the color of the majority of the views, hence we'll use it
// to set our default theme.
final boolean lockDarkText = mColorExtractor.getNeutralColors().supportsDarkText();
final int themeResId = lockDarkText ? R.style.Theme_SystemUI_LightWallpaper
: R.style.Theme_SystemUI;
- if (mContext.getThemeResId() == themeResId) {
- return;
+ if (mContext.getThemeResId() != themeResId) {
+ mContext.setTheme(themeResId);
+ mConfigurationController.notifyThemeChanged();
}
- mContext.setTheme(themeResId);
- mConfigurationController.notifyThemeChanged();
}
private void updateDozingState() {
@@ -3293,7 +3330,10 @@ public class StatusBar extends CoreStartable implements
}
private void showBouncerOrLockScreenIfKeyguard() {
- if (!mKeyguardViewMediator.isHiding()) {
+ // If the keyguard is animating away, we aren't really the keyguard anymore and should not
+ // show the bouncer/lockscreen.
+ if (!mKeyguardViewMediator.isHiding()
+ && !mKeyguardUnlockAnimationController.isPlayingCannedUnlockAnimation()) {
if (mState == StatusBarState.SHADE_LOCKED
&& mKeyguardUpdateMonitor.isUdfpsEnrolled()) {
// shade is showing while locked on the keyguard, so go back to showing the
@@ -3631,6 +3671,10 @@ public class StatusBar extends CoreStartable implements
public void onScreenTurnedOff() {
mFalsingCollector.onScreenOff();
mScrimController.onScreenTurnedOff();
+ if (mCloseQsBeforeScreenOff) {
+ mNotificationPanelViewController.closeQs();
+ mCloseQsBeforeScreenOff = false;
+ }
updateIsKeyguard();
}
};
@@ -3723,17 +3767,14 @@ public class StatusBar extends CoreStartable implements
public void updateScrimController() {
Trace.beginSection("StatusBar#updateScrimController");
- // We don't want to end up in KEYGUARD state when we're unlocking with
- // fingerprint from doze. We should cross fade directly from black.
- boolean unlocking = mBiometricUnlockController.isWakeAndUnlock()
- || mKeyguardStateController.isKeyguardFadingAway();
+ boolean unlocking = mKeyguardStateController.isShowing() && (
+ mBiometricUnlockController.isWakeAndUnlock()
+ || mKeyguardStateController.isKeyguardFadingAway()
+ || mKeyguardStateController.isKeyguardGoingAway()
+ || mKeyguardViewMediator.requestedShowSurfaceBehindKeyguard()
+ || mKeyguardViewMediator.isAnimatingBetweenKeyguardAndSurfaceBehind());
- // Do not animate the scrim expansion when triggered by the fingerprint sensor.
- boolean onKeyguardOrHidingIt = mKeyguardStateController.isShowing()
- || mKeyguardStateController.isKeyguardFadingAway()
- || mKeyguardStateController.isKeyguardGoingAway();
- mScrimController.setExpansionAffectsAlpha(!(mBiometricUnlockController.isBiometricUnlock()
- && onKeyguardOrHidingIt));
+ mScrimController.setExpansionAffectsAlpha(!unlocking);
boolean launchingAffordanceWithPreview =
mNotificationPanelViewController.isLaunchingAffordanceWithPreview();
@@ -3745,7 +3786,7 @@ public class StatusBar extends CoreStartable implements
} else {
mScrimController.transitionTo(ScrimState.AUTH_SCRIMMED);
}
- } else if (mBouncerShowing) {
+ } else if (mBouncerShowing && !unlocking) {
// Bouncer needs the front scrim when it's on top of an activity,
// tapping on a notification, editing QS or being dismissed by
// FLAG_DISMISS_KEYGUARD_ACTIVITY.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
index ea0dd72d673f..cc65ca025139 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
@@ -6,6 +6,7 @@ import android.animation.ValueAnimator
import android.content.Context
import android.database.ContentObserver
import android.os.Handler
+import android.os.PowerManager
import android.provider.Settings
import android.view.Surface
import android.view.View
@@ -54,9 +55,10 @@ class UnlockedScreenOffAnimationController @Inject constructor(
private val keyguardStateController: KeyguardStateController,
private val dozeParameters: dagger.Lazy<DozeParameters>,
private val globalSettings: GlobalSettings,
- private val interactionJankMonitor: InteractionJankMonitor
+ private val interactionJankMonitor: InteractionJankMonitor,
+ private val powerManager: PowerManager,
+ private val handler: Handler = Handler()
) : WakefulnessLifecycle.Observer, ScreenOffAnimation {
- private val handler = Handler()
private lateinit var statusBar: StatusBar
private lateinit var lightRevealScrim: LightRevealScrim
@@ -219,7 +221,7 @@ class UnlockedScreenOffAnimationController @Inject constructor(
// even if we're going from SHADE to SHADE or KEYGUARD to KEYGUARD, since we might have
// changed parts of the UI (such as showing AOD in the shade) without actually changing
// the StatusBarState. This ensures that the UI definitely reflects the desired state.
- statusBar.updateIsKeyguard(true /* force */)
+ statusBar.updateIsKeyguard(true /* forceStateChange */)
}
}
@@ -231,10 +233,18 @@ class UnlockedScreenOffAnimationController @Inject constructor(
lightRevealAnimationPlaying = true
lightRevealAnimator.start()
handler.postDelayed({
- aodUiAnimationPlaying = true
-
- // Show AOD. That'll cause the KeyguardVisibilityHelper to call #animateInKeyguard.
- statusBar.notificationPanelViewController.showAodUi()
+ // Only run this callback if the device is sleeping (not interactive). This callback
+ // is removed in onStartedWakingUp, but since that event is asynchronously
+ // dispatched, a race condition could make it possible for this callback to be run
+ // as the device is waking up. That results in the AOD UI being shown while we wake
+ // up, with unpredictable consequences.
+ if (!powerManager.isInteractive) {
+ aodUiAnimationPlaying = true
+
+ // Show AOD. That'll cause the KeyguardVisibilityHelper to call
+ // #animateInKeyguard.
+ statusBar.notificationPanelViewController.showAodUi()
+ }
}, (ANIMATE_IN_KEYGUARD_DELAY * animatorDurationScale).toLong())
return true
@@ -290,6 +300,9 @@ class UnlockedScreenOffAnimationController @Inject constructor(
return true
}
+ override fun shouldDelayDisplayDozeTransition(): Boolean =
+ dozeParameters.get().shouldControlUnlockedScreenOff()
+
fun addCallback(callback: Callback) {
callbacks.add(callback)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
index 1030bfdb40fd..33f2140b150e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
@@ -127,10 +127,12 @@ public final class DeviceStateRotationLockSettingController
int rotationLockSetting =
mDeviceStateRotationLockSettingsManager.getRotationLockSetting(state);
if (rotationLockSetting == DEVICE_STATE_ROTATION_LOCK_IGNORED) {
+ // This should not happen. Device states that have an ignored setting, should also
+ // specify a fallback device state which is not ignored.
// We won't handle this device state. The same rotation lock setting as before should
// apply and any changes to the rotation lock setting will be written for the previous
// valid device state.
- Log.v(TAG, "Ignoring new device state: " + state);
+ Log.w(TAG, "Missing fallback. Ignoring new device state: " + state);
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingsManager.java
index a418c74848a5..bec5fc8e7b9f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingsManager.java
@@ -52,12 +52,14 @@ public final class DeviceStateRotationLockSettingsManager {
private final Handler mMainHandler = Handler.getMain();
private final Set<DeviceStateRotationLockSettingsListener> mListeners = new HashSet<>();
private SparseIntArray mDeviceStateRotationLockSettings;
+ private SparseIntArray mDeviceStateRotationLockFallbackSettings;
private DeviceStateRotationLockSettingsManager(Context context) {
mContentResolver = context.getContentResolver();
mDeviceStateRotationLockDefaults =
context.getResources()
.getStringArray(R.array.config_perDeviceStateRotationLockDefaults);
+ loadDefaults();
initializeInMemoryMap();
listenForSettingsChange(context);
}
@@ -114,6 +116,11 @@ public final class DeviceStateRotationLockSettingsManager {
/** Updates the rotation lock setting for a specified device state. */
public void updateSetting(int deviceState, boolean rotationLocked) {
+ if (mDeviceStateRotationLockFallbackSettings.indexOfKey(deviceState) >= 0) {
+ // The setting for this device state is IGNORED, and has a fallback device state.
+ // The setting for that fallback device state should be the changed in this case.
+ deviceState = mDeviceStateRotationLockFallbackSettings.get(deviceState);
+ }
mDeviceStateRotationLockSettings.put(
deviceState,
rotationLocked
@@ -123,16 +130,37 @@ public final class DeviceStateRotationLockSettingsManager {
}
/**
- * Returns the {@link DeviceStateRotationLockSetting} for the given device state. If no setting
- * is specified for this device state, it will return {@link
+ * Returns the {@link Settings.Secure.DeviceStateRotationLockSetting} for the given device
+ * state.
+ *
+ * <p>If the setting for this device state is {@link DEVICE_STATE_ROTATION_LOCK_IGNORED}, it
+ * will return the setting for the fallback device state.
+ *
+ * <p>If no fallback is specified for this device state, it will return {@link
* DEVICE_STATE_ROTATION_LOCK_IGNORED}.
*/
@Settings.Secure.DeviceStateRotationLockSetting
public int getRotationLockSetting(int deviceState) {
- return mDeviceStateRotationLockSettings.get(
- deviceState, DEVICE_STATE_ROTATION_LOCK_IGNORED);
+ int rotationLockSetting = mDeviceStateRotationLockSettings.get(
+ deviceState, /* valueIfKeyNotFound= */ DEVICE_STATE_ROTATION_LOCK_IGNORED);
+ if (rotationLockSetting == DEVICE_STATE_ROTATION_LOCK_IGNORED) {
+ rotationLockSetting = getFallbackRotationLockSetting(deviceState);
+ }
+ return rotationLockSetting;
+ }
+
+ private int getFallbackRotationLockSetting(int deviceState) {
+ int indexOfFallbackState = mDeviceStateRotationLockFallbackSettings.indexOfKey(deviceState);
+ if (indexOfFallbackState < 0) {
+ Log.w(TAG, "Setting is ignored, but no fallback was specified.");
+ return DEVICE_STATE_ROTATION_LOCK_IGNORED;
+ }
+ int fallbackState = mDeviceStateRotationLockFallbackSettings.valueAt(indexOfFallbackState);
+ return mDeviceStateRotationLockSettings.get(fallbackState,
+ /* valueIfKeyNotFound= */ DEVICE_STATE_ROTATION_LOCK_IGNORED);
}
+
/** Returns true if the rotation is locked for the current device state */
public boolean isRotationLocked(int deviceState) {
return getRotationLockSetting(deviceState) == DEVICE_STATE_ROTATION_LOCK_LOCKED;
@@ -223,21 +251,30 @@ public final class DeviceStateRotationLockSettingsManager {
}
private void loadDefaults() {
- if (mDeviceStateRotationLockDefaults.length == 0) {
- Log.w(TAG, "Empty default settings");
- mDeviceStateRotationLockSettings = new SparseIntArray(/* initialCapacity= */ 0);
- return;
- }
- mDeviceStateRotationLockSettings =
- new SparseIntArray(mDeviceStateRotationLockDefaults.length);
- for (String serializedDefault : mDeviceStateRotationLockDefaults) {
- String[] entry = serializedDefault.split(SEPARATOR_REGEX);
+ mDeviceStateRotationLockSettings = new SparseIntArray(
+ mDeviceStateRotationLockDefaults.length);
+ mDeviceStateRotationLockFallbackSettings = new SparseIntArray(1);
+ for (String entry : mDeviceStateRotationLockDefaults) {
+ String[] values = entry.split(SEPARATOR_REGEX);
try {
- int key = Integer.parseInt(entry[0]);
- int value = Integer.parseInt(entry[1]);
- mDeviceStateRotationLockSettings.put(key, value);
+ int deviceState = Integer.parseInt(values[0]);
+ int rotationLockSetting = Integer.parseInt(values[1]);
+ if (rotationLockSetting == DEVICE_STATE_ROTATION_LOCK_IGNORED) {
+ if (values.length == 3) {
+ int fallbackDeviceState = Integer.parseInt(values[2]);
+ mDeviceStateRotationLockFallbackSettings.put(deviceState,
+ fallbackDeviceState);
+ } else {
+ Log.w(TAG,
+ "Rotation lock setting is IGNORED, but values have unexpected "
+ + "size of "
+ + values.length);
+ }
+ }
+ mDeviceStateRotationLockSettings.put(deviceState, rotationLockSetting);
} catch (NumberFormatException e) {
- Log.wtf(TAG, "Error deserializing default settings", e);
+ Log.wtf(TAG, "Error parsing settings entry. Entry was: " + entry, e);
+ return;
}
}
}
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 7bf1601b2fd5..050b67016d09 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java
@@ -50,13 +50,6 @@ public interface KeyguardStateController extends CallbackController<Callback> {
boolean canDismissLockScreen();
/**
- * Whether we can currently perform the shared element SmartSpace transition. This is true if
- * we're on the lockscreen, it can be dismissed with a swipe, and the Launcher is underneath the
- * keyguard and displaying a SmartSpace that it has registered with System UI.
- */
- boolean canPerformSmartSpaceTransition();
-
- /**
* Whether the keyguard is allowed to rotate, or needs to be locked to the default orientation.
*/
boolean isKeyguardScreenRotationAllowed();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
index 05a586b1cdc2..978564fc81d0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
@@ -35,7 +35,7 @@ import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController;
+import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -44,6 +44,8 @@ import java.util.Objects;
import javax.inject.Inject;
+import dagger.Lazy;
+
/**
*/
@SysUISingleton
@@ -58,7 +60,7 @@ public class KeyguardStateControllerImpl implements KeyguardStateController, Dum
private final LockPatternUtils mLockPatternUtils;
private final KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback =
new UpdateMonitorCallback();
- private final SmartspaceTransitionController mSmartspaceTransitionController;
+ private final Lazy<KeyguardUnlockAnimationController> mUnlockAnimationControllerLazy;
private boolean mCanDismissLockScreen;
private boolean mShowing;
@@ -105,13 +107,13 @@ public class KeyguardStateControllerImpl implements KeyguardStateController, Dum
Context context,
KeyguardUpdateMonitor keyguardUpdateMonitor,
LockPatternUtils lockPatternUtils,
- SmartspaceTransitionController smartspaceTransitionController,
+ Lazy<KeyguardUnlockAnimationController> keyguardUnlockAnimationController,
DumpManager dumpManager) {
mContext = context;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mLockPatternUtils = lockPatternUtils;
mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback);
- mSmartspaceTransitionController = smartspaceTransitionController;
+ mUnlockAnimationControllerLazy = keyguardUnlockAnimationController;
dumpManager.registerDumpable(getClass().getSimpleName(), this);
@@ -249,12 +251,6 @@ public class KeyguardStateControllerImpl implements KeyguardStateController, Dum
}
@Override
- public boolean canPerformSmartSpaceTransition() {
- return canDismissLockScreen()
- && mSmartspaceTransitionController.isSmartspaceTransitionPossible();
- }
-
- @Override
public boolean isKeyguardScreenRotationAllowed() {
return SystemProperties.getBoolean("lockscreen.rot_override", false)
|| mContext.getResources().getBoolean(R.bool.config_enableLockScreenRotation);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
index 3831857c5c8d..59969c0447b3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
@@ -22,10 +22,14 @@ import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION;
import static com.android.settingslib.Utils.updateLocationEnabled;
+import android.app.AppOpsManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.PermissionChecker;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
import android.location.LocationManager;
import android.os.Handler;
import android.os.Looper;
@@ -68,31 +72,37 @@ public class LocationControllerImpl extends BroadcastReceiver implements Locatio
private final BootCompleteCache mBootCompleteCache;
private final UserTracker mUserTracker;
private final H mHandler;
-
+ private final Handler mBackgroundHandler;
+ private final PackageManager mPackageManager;
private boolean mAreActiveLocationRequests;
private boolean mShouldDisplayAllAccesses;
+ private boolean mShowSystemAccesses;
@Inject
public LocationControllerImpl(Context context, AppOpsController appOpsController,
DeviceConfigProxy deviceConfigProxy,
@Main Looper mainLooper, @Background Handler backgroundHandler,
BroadcastDispatcher broadcastDispatcher, BootCompleteCache bootCompleteCache,
- UserTracker userTracker) {
+ UserTracker userTracker, PackageManager packageManager) {
mContext = context;
mAppOpsController = appOpsController;
mDeviceConfigProxy = deviceConfigProxy;
mBootCompleteCache = bootCompleteCache;
mHandler = new H(mainLooper);
mUserTracker = userTracker;
- mShouldDisplayAllAccesses = getDeviceConfigSetting();
+ mBackgroundHandler = backgroundHandler;
+ mPackageManager = packageManager;
+ mShouldDisplayAllAccesses = getAllAccessesSetting();
+ mShowSystemAccesses = getShowSystemSetting();
// Register to listen for changes in DeviceConfig settings.
mDeviceConfigProxy.addOnPropertiesChangedListener(
DeviceConfig.NAMESPACE_PRIVACY,
backgroundHandler::post,
properties -> {
- mShouldDisplayAllAccesses = getDeviceConfigSetting();
+ mShouldDisplayAllAccesses = getAllAccessesSetting();
+ mShowSystemAccesses = getShowSystemSetting();
updateActiveLocationRequests();
});
@@ -176,11 +186,15 @@ public class LocationControllerImpl extends BroadcastReceiver implements Locatio
UserHandle.of(userId));
}
- private boolean getDeviceConfigSetting() {
+ private boolean getAllAccessesSetting() {
return mDeviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
SystemUiDeviceConfigFlags.PROPERTY_LOCATION_INDICATORS_SMALL_ENABLED, false);
}
+ private boolean getShowSystemSetting() {
+ return mDeviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
+ SystemUiDeviceConfigFlags.PROPERTY_LOCATION_INDICATORS_SHOW_SYSTEM, false);
+ }
/**
* Returns true if there currently exist active high power location requests.
*/
@@ -202,35 +216,74 @@ public class LocationControllerImpl extends BroadcastReceiver implements Locatio
* Returns true if there currently exist active location requests.
*/
@VisibleForTesting
- protected boolean areActiveLocationRequests() {
+ protected void areActiveLocationRequests() {
if (!mShouldDisplayAllAccesses) {
- return false;
+ return;
}
- List<AppOpItem> appOpsItems = mAppOpsController.getActiveAppOps();
+ boolean hadActiveLocationRequests = mAreActiveLocationRequests;
+ boolean shouldDisplay = false;
+ List<AppOpItem> appOpsItems = mAppOpsController.getActiveAppOps();
+ final List<UserInfo> profiles = mUserTracker.getUserProfiles();
final int numItems = appOpsItems.size();
for (int i = 0; i < numItems; i++) {
if (appOpsItems.get(i).getCode() == OP_FINE_LOCATION
|| appOpsItems.get(i).getCode() == OP_COARSE_LOCATION) {
- return true;
+ if (mShowSystemAccesses) {
+ shouldDisplay = true;
+ } else {
+ shouldDisplay |= !isSystemApp(profiles, appOpsItems.get(i));
+ }
}
}
- return false;
+ mAreActiveLocationRequests = areActiveHighPowerLocationRequests() || shouldDisplay;
+ if (mAreActiveLocationRequests != hadActiveLocationRequests) {
+ mHandler.sendEmptyMessage(H.MSG_LOCATION_ACTIVE_CHANGED);
+ }
+ }
+
+ private boolean isSystemApp(List<UserInfo> profiles, AppOpItem item) {
+ final String permission = AppOpsManager.opToPermission(item.getCode());
+ UserHandle user = UserHandle.getUserHandleForUid(item.getUid());
+
+ // Don't show apps belonging to background users except managed users.
+ boolean foundUser = false;
+ final int numProfiles = profiles.size();
+ for (int i = 0; i < numProfiles; i++) {
+ if (profiles.get(i).getUserHandle().equals(user)) {
+ foundUser = true;
+ }
+ }
+ if (!foundUser) {
+ return true;
+ }
+
+ final int permissionFlags = mPackageManager.getPermissionFlags(
+ permission, item.getPackageName(), user);
+ if (PermissionChecker.checkPermissionForPreflight(mContext, permission,
+ PermissionChecker.PID_UNKNOWN, item.getUid(), item.getPackageName())
+ == PermissionChecker.PERMISSION_GRANTED) {
+ return (permissionFlags
+ & PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED)
+ == 0;
+ } else {
+ return (permissionFlags
+ & PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED) == 0;
+ }
}
// Reads the active location requests from either OP_MONITOR_HIGH_POWER_LOCATION,
// OP_FINE_LOCATION, or OP_COARSE_LOCATION and updates the status view if necessary.
private void updateActiveLocationRequests() {
- boolean hadActiveLocationRequests = mAreActiveLocationRequests;
if (mShouldDisplayAllAccesses) {
- mAreActiveLocationRequests =
- areActiveHighPowerLocationRequests() || areActiveLocationRequests();
+ mBackgroundHandler.post(this::areActiveLocationRequests);
} else {
+ boolean hadActiveLocationRequests = mAreActiveLocationRequests;
mAreActiveLocationRequests = areActiveHighPowerLocationRequests();
- }
- if (mAreActiveLocationRequests != hadActiveLocationRequests) {
- mHandler.sendEmptyMessage(H.MSG_LOCATION_ACTIVE_CHANGED);
+ if (mAreActiveLocationRequests != hadActiveLocationRequests) {
+ mHandler.sendEmptyMessage(H.MSG_LOCATION_ACTIVE_CHANGED);
+ }
}
}
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 46fa20d094a0..48949f92413d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -51,6 +51,7 @@ import android.view.ViewAnimationUtils;
import android.view.ViewGroup;
import android.view.WindowInsets;
import android.view.WindowInsetsAnimation;
+import android.view.WindowInsetsController;
import android.view.accessibility.AccessibilityEvent;
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.EditorInfo;
@@ -665,8 +666,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
}
// Hide soft-keyboard when the input view became invisible
// (i.e. The notification shade collapsed by pressing the home key)
- if (visibility != VISIBLE && !mEditText.isVisibleToUser()
- && !mController.isRemoteInputActive()) {
+ if (visibility != VISIBLE && !mController.isRemoteInputActive()) {
mEditText.hideIme();
}
}
@@ -779,8 +779,9 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
}
private void hideIme() {
- if (mInputMethodManager != null) {
- mInputMethodManager.hideSoftInputFromWindow(getWindowToken(), 0);
+ final WindowInsetsController insetsController = getWindowInsetsController();
+ if (insetsController != null) {
+ insetsController.hide(WindowInsets.Type.ime());
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt b/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
index 52c416bad803..aaf35afe936d 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
@@ -30,17 +30,17 @@ import dagger.Lazy
import javax.inject.Inject
/**
- * Controls folding to AOD animation: when AOD is enabled and foldable device is folded
- * we play a special AOD animation on the outer screen
+ * Controls folding to AOD animation: when AOD is enabled and foldable device is folded we play a
+ * special AOD animation on the outer screen
*/
@SysUIUnfoldScope
-class FoldAodAnimationController @Inject constructor(
+class FoldAodAnimationController
+@Inject
+constructor(
private val keyguardViewMediatorLazy: Lazy<KeyguardViewMediator>,
private val wakefulnessLifecycle: WakefulnessLifecycle,
private val globalSettings: GlobalSettings
-) : CallbackController<FoldAodAnimationStatus>,
- ScreenOffAnimation,
- WakefulnessLifecycle.Observer {
+) : CallbackController<FoldAodAnimationStatus>, ScreenOffAnimation, WakefulnessLifecycle.Observer {
private var alwaysOnEnabled: Boolean = false
private var isScrimOpaque: Boolean = false
@@ -58,17 +58,13 @@ class FoldAodAnimationController @Inject constructor(
wakefulnessLifecycle.addObserver(this)
}
- /**
- * Returns true if we should run fold to AOD animation
- */
- override fun shouldPlayAnimation(): Boolean =
- shouldPlayAnimation
+ /** Returns true if we should run fold to AOD animation */
+ override fun shouldPlayAnimation(): Boolean = shouldPlayAnimation
override fun startAnimation(): Boolean =
if (alwaysOnEnabled &&
wakefulnessLifecycle.lastSleepReason == PowerManager.GO_TO_SLEEP_REASON_DEVICE_FOLD &&
- globalSettings.getString(Settings.Global.ANIMATOR_DURATION_SCALE) != "0"
- ) {
+ globalSettings.getString(Settings.Global.ANIMATOR_DURATION_SCALE) != "0") {
shouldPlayAnimation = true
isAnimationPlaying = true
@@ -107,9 +103,7 @@ class FoldAodAnimationController @Inject constructor(
}
}
- /**
- * Called when keyguard scrim opaque changed
- */
+ /** Called when keyguard scrim opaque changed */
override fun onScrimOpaqueChanged(isOpaque: Boolean) {
isScrimOpaque = isOpaque
@@ -130,27 +124,21 @@ class FoldAodAnimationController @Inject constructor(
}
}
- override fun isAnimationPlaying(): Boolean =
- isAnimationPlaying
+ override fun isAnimationPlaying(): Boolean = isAnimationPlaying
- override fun isKeyguardHideDelayed(): Boolean =
- isAnimationPlaying()
+ override fun isKeyguardHideDelayed(): Boolean = isAnimationPlaying()
- override fun shouldShowAodIconsWhenShade(): Boolean =
- shouldPlayAnimation()
+ override fun shouldShowAodIconsWhenShade(): Boolean = shouldPlayAnimation()
- override fun shouldAnimateAodIcons(): Boolean =
- !shouldPlayAnimation()
+ override fun shouldAnimateAodIcons(): Boolean = !shouldPlayAnimation()
- override fun shouldAnimateDozingChange(): Boolean =
- !shouldPlayAnimation()
+ override fun shouldAnimateDozingChange(): Boolean = !shouldPlayAnimation()
- override fun shouldAnimateClockChange(): Boolean =
- !isAnimationPlaying()
+ override fun shouldAnimateClockChange(): Boolean = !isAnimationPlaying()
- /**
- * Called when AOD status is changed
- */
+ override fun shouldDelayDisplayDozeTransition(): Boolean = shouldPlayAnimation()
+
+ /** Called when AOD status is changed */
override fun onAlwaysOnChanged(alwaysOn: Boolean) {
alwaysOnEnabled = alwaysOn
}
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLatencyTracker.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLatencyTracker.kt
index 7f63d6c5e778..79b42b8daab1 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLatencyTracker.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLatencyTracker.kt
@@ -29,12 +29,16 @@ import javax.inject.Inject
* Logs performance metrics regarding time to turn the inner screen on.
*
* This class assumes that [onFoldEvent] is always called before [onScreenTurnedOn].
+ *
* This should be used from only one process.
+ *
* For now, the focus is on the time the inner display is visible, but in the future, it is easily
* possible to monitor the time to go from the inner screen to the outer.
*/
@SysUISingleton
-class UnfoldLatencyTracker @Inject constructor(
+class UnfoldLatencyTracker
+@Inject
+constructor(
private val latencyTracker: LatencyTracker,
private val deviceStateManager: DeviceStateManager,
@UiBackground private val uiBgExecutor: Executor,
@@ -45,8 +49,11 @@ class UnfoldLatencyTracker @Inject constructor(
private var folded: Boolean? = null
private val foldStateListener = FoldStateListener(context)
private val isFoldable: Boolean
- get() = context.resources.getIntArray(
- com.android.internal.R.array.config_foldedDeviceStates).isNotEmpty()
+ get() =
+ context
+ .resources
+ .getIntArray(com.android.internal.R.array.config_foldedDeviceStates)
+ .isNotEmpty()
/** Registers for relevant events only if the device is foldable. */
fun init() {
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
index 0b89ef28d227..4b09a583645c 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
@@ -20,8 +20,8 @@ import android.content.Context
import android.graphics.PixelFormat
import android.hardware.devicestate.DeviceStateManager
import android.hardware.devicestate.DeviceStateManager.FoldStateListener
-import android.hardware.input.InputManager
import android.hardware.display.DisplayManager
+import android.hardware.input.InputManager
import android.os.Handler
import android.os.Trace
import android.view.Choreographer
@@ -46,7 +46,9 @@ import java.util.function.Consumer
import javax.inject.Inject
@SysUIUnfoldScope
-class UnfoldLightRevealOverlayAnimation @Inject constructor(
+class UnfoldLightRevealOverlayAnimation
+@Inject
+constructor(
private val context: Context,
private val deviceStateManager: DeviceStateManager,
private val displayManager: DisplayManager,
@@ -75,12 +77,13 @@ class UnfoldLightRevealOverlayAnimation @Inject constructor(
deviceStateManager.registerCallback(executor, FoldListener())
unfoldTransitionProgressProvider.addCallback(transitionListener)
- val containerBuilder = SurfaceControl.Builder(SurfaceSession())
- .setContainerLayer()
- .setName("unfold-overlay-container")
+ val containerBuilder =
+ SurfaceControl.Builder(SurfaceSession())
+ .setContainerLayer()
+ .setName("unfold-overlay-container")
- displayAreaHelper.get().attachToRootDisplayArea(Display.DEFAULT_DISPLAY,
- containerBuilder) { builder ->
+ displayAreaHelper.get().attachToRootDisplayArea(
+ Display.DEFAULT_DISPLAY, containerBuilder) { builder ->
executor.execute {
overlayContainer = builder.build()
@@ -89,13 +92,13 @@ class UnfoldLightRevealOverlayAnimation @Inject constructor(
.show(overlayContainer)
.apply()
- wwm = WindowlessWindowManager(context.resources.configuration,
- overlayContainer, null)
+ wwm =
+ WindowlessWindowManager(context.resources.configuration, overlayContainer, null)
}
}
- displayManager.registerDisplayListener(displayListener, handler,
- DisplayManager.EVENT_FLAG_DISPLAY_CHANGED)
+ displayManager.registerDisplayListener(
+ displayListener, handler, DisplayManager.EVENT_FLAG_DISPLAY_CHANGED)
// Get unfolded display size immediately as 'current display info' might be
// not up-to-date during unfolding
@@ -136,8 +139,8 @@ class UnfoldLightRevealOverlayAnimation @Inject constructor(
ensureOverlayRemoved()
val newRoot = SurfaceControlViewHost(context, context.display!!, wwm, false)
- val newView = LightRevealScrim(context, null)
- .apply {
+ val newView =
+ LightRevealScrim(context, null).apply {
revealEffect = createLightRevealEffect()
isScrimOpaqueChangedListener = Consumer {}
revealAmount = 0f
@@ -147,8 +150,7 @@ class UnfoldLightRevealOverlayAnimation @Inject constructor(
newRoot.setView(newView, params)
onOverlayReady?.let { callback ->
- Trace.beginAsyncSection(
- "UnfoldLightRevealOverlayAnimation#relayout", 0)
+ Trace.beginAsyncSection("UnfoldLightRevealOverlayAnimation#relayout", 0)
newRoot.relayout(params) { transaction ->
val vsyncId = Choreographer.getSfInstance().vsyncId
@@ -161,15 +163,11 @@ class UnfoldLightRevealOverlayAnimation @Inject constructor(
// (turn on the brightness) only when the content is actually visible as it
// might be presented only in the next frame.
// See b/197538198
- transaction.setFrameTimelineVsync(vsyncId)
- .apply(/* sync */true)
+ transaction.setFrameTimelineVsync(vsyncId).apply(/* sync */ true)
- transaction
- .setFrameTimelineVsync(vsyncId + 1)
- .apply(/* sync */ true)
+ transaction.setFrameTimelineVsync(vsyncId + 1).apply(/* sync */ true)
- Trace.endAsyncSection(
- "UnfoldLightRevealOverlayAnimation#relayout", 0)
+ Trace.endAsyncSection("UnfoldLightRevealOverlayAnimation#relayout", 0)
callback.run()
}
}
@@ -185,10 +183,10 @@ class UnfoldLightRevealOverlayAnimation @Inject constructor(
val rotation = context.display!!.rotation
val isNatural = rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180
- params.height = if (isNatural)
- unfoldedDisplayInfo.naturalHeight else unfoldedDisplayInfo.naturalWidth
- params.width = if (isNatural)
- unfoldedDisplayInfo.naturalWidth else unfoldedDisplayInfo.naturalHeight
+ params.height =
+ if (isNatural) unfoldedDisplayInfo.naturalHeight else unfoldedDisplayInfo.naturalWidth
+ params.width =
+ if (isNatural) unfoldedDisplayInfo.naturalWidth else unfoldedDisplayInfo.naturalHeight
params.format = PixelFormat.TRANSLUCENT
params.type = WindowManager.LayoutParams.TYPE_DISPLAY_OVERLAY
@@ -206,8 +204,8 @@ class UnfoldLightRevealOverlayAnimation @Inject constructor(
}
private fun createLightRevealEffect(): LightRevealEffect {
- val isVerticalFold = currentRotation == Surface.ROTATION_0 ||
- currentRotation == Surface.ROTATION_180
+ val isVerticalFold =
+ currentRotation == Surface.ROTATION_0 || currentRotation == Surface.ROTATION_180
return LinearLightRevealEffect(isVertical = isVerticalFold)
}
@@ -218,7 +216,8 @@ class UnfoldLightRevealOverlayAnimation @Inject constructor(
}
private fun getUnfoldedDisplayInfo(): DisplayInfo =
- displayManager.displays
+ displayManager
+ .displays
.asSequence()
.map { DisplayInfo().apply { it.getDisplayInfo(this) } }
.filter { it.type == Display.TYPE_INTERNAL }
@@ -255,18 +254,19 @@ class UnfoldLightRevealOverlayAnimation @Inject constructor(
}
}
- override fun onDisplayAdded(displayId: Int) {
- }
+ override fun onDisplayAdded(displayId: Int) {}
- override fun onDisplayRemoved(displayId: Int) {
- }
+ override fun onDisplayRemoved(displayId: Int) {}
}
- private inner class FoldListener : FoldStateListener(context, Consumer { isFolded ->
- if (isFolded) {
- ensureOverlayRemoved()
- isUnfoldHandled = false
- }
- this.isFolded = isFolded
- })
+ private inner class FoldListener :
+ FoldStateListener(
+ context,
+ Consumer { isFolded ->
+ if (isFolded) {
+ ensureOverlayRemoved()
+ isUnfoldHandled = false
+ }
+ this.isFolded = isFolded
+ })
}
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldProgressProvider.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldProgressProvider.kt
index bd04ad8385b2..2325acfdcd48 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldProgressProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldProgressProvider.kt
@@ -21,29 +21,23 @@ import com.android.wm.shell.unfold.ShellUnfoldProgressProvider
import com.android.wm.shell.unfold.ShellUnfoldProgressProvider.UnfoldListener
import java.util.concurrent.Executor
-class UnfoldProgressProvider(
- private val unfoldProgressProvider: UnfoldTransitionProgressProvider
-) : ShellUnfoldProgressProvider {
+class UnfoldProgressProvider(private val unfoldProgressProvider: UnfoldTransitionProgressProvider) :
+ ShellUnfoldProgressProvider {
override fun addListener(executor: Executor, listener: UnfoldListener) {
- unfoldProgressProvider.addCallback(object : TransitionProgressListener {
- override fun onTransitionStarted() {
- executor.execute {
- listener.onStateChangeStarted()
+ unfoldProgressProvider.addCallback(
+ object : TransitionProgressListener {
+ override fun onTransitionStarted() {
+ executor.execute { listener.onStateChangeStarted() }
}
- }
- override fun onTransitionProgress(progress: Float) {
- executor.execute {
- listener.onStateChangeProgress(progress)
+ override fun onTransitionProgress(progress: Float) {
+ executor.execute { listener.onStateChangeProgress(progress) }
}
- }
- override fun onTransitionFinished() {
- executor.execute {
- listener.onStateChangeFinished()
+ override fun onTransitionFinished() {
+ executor.execute { listener.onStateChangeFinished() }
}
- }
- })
+ })
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
index 178d01477a09..d2d2361d613d 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
@@ -90,7 +90,7 @@ class UnfoldTransitionModule {
config: UnfoldTransitionConfig,
provider: Optional<UnfoldTransitionProgressProvider>
): ShellUnfoldProgressProvider =
- if (config.isEnabled && provider.isPresent()) {
+ if (config.isEnabled && provider.isPresent) {
UnfoldProgressProvider(provider.get())
} else {
ShellUnfoldProgressProvider.NO_PROVIDER
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionWallpaperController.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionWallpaperController.kt
index a184315ab75c..d723760fa510 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionWallpaperController.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionWallpaperController.kt
@@ -21,7 +21,9 @@ import com.android.systemui.util.WallpaperController
import javax.inject.Inject
@SysUIUnfoldScope
-class UnfoldTransitionWallpaperController @Inject constructor(
+class UnfoldTransitionWallpaperController
+@Inject
+constructor(
private val unfoldTransitionProgressProvider: UnfoldTransitionProgressProvider,
private val wallpaperController: WallpaperController
) {
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
index 20c8bf38692b..69ebfe8012d1 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
@@ -427,9 +427,11 @@ public class BubblesManager implements Dumpable {
}
@Override
- public void onEntryRemoved(NotificationEntry entry,
+ public void onEntryRemoved(
+ NotificationEntry entry,
@Nullable NotificationVisibility visibility,
- boolean removedByUser, int reason) {
+ boolean removedByUser,
+ int reason) {
BubblesManager.this.onEntryRemoved(entry);
}
@@ -437,6 +439,18 @@ public class BubblesManager implements Dumpable {
public void onNotificationRankingUpdated(RankingMap rankingMap) {
BubblesManager.this.onRankingUpdate(rankingMap);
}
+
+ @Override
+ public void onNotificationChannelModified(
+ String pkgName,
+ UserHandle user,
+ NotificationChannel channel,
+ int modificationType) {
+ BubblesManager.this.onNotificationChannelModified(pkgName,
+ user,
+ channel,
+ modificationType);
+ }
});
// The new pipeline takes care of this as a NotifDismissInterceptor BubbleCoordinator
@@ -556,6 +570,19 @@ public class BubblesManager implements Dumpable {
public void onRankingUpdate(RankingMap rankingMap) {
BubblesManager.this.onRankingUpdate(rankingMap);
}
+
+ @Override
+ public void onNotificationChannelModified(
+ String pkgName,
+ UserHandle user,
+ NotificationChannel channel,
+ int modificationType) {
+ BubblesManager.this.onNotificationChannelModified(
+ pkgName,
+ user,
+ channel,
+ modificationType);
+ }
});
}
@@ -592,6 +619,14 @@ public class BubblesManager implements Dumpable {
mBubbles.onRankingUpdated(rankingMap, pendingOrActiveNotif);
}
+ void onNotificationChannelModified(
+ String pkg,
+ UserHandle user,
+ NotificationChannel channel,
+ int modificationType) {
+ mBubbles.onNotificationChannelModified(pkg, user, channel, modificationType);
+ }
+
/**
* Gets the DismissedByUserStats used by {@link NotificationEntryManager}.
* Will not be necessary when using the new notification pipeline's {@link NotifCollection}.
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
index 74e0f4002026..c2439df6624a 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
@@ -48,7 +48,6 @@ import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.plugins.ClockPlugin;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
@@ -96,9 +95,8 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase {
@Mock
Resources mResources;
- KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
@Mock
- SmartspaceTransitionController mSmartSpaceTransitionController;
+ KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
@Mock
private ClockPlugin mClockPlugin;
@Mock
@@ -154,7 +152,6 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase {
mBypassController,
mSmartspaceController,
mKeyguardUnlockAnimationController,
- mSmartSpaceTransitionController,
mSecureSettings,
mExecutor,
mResources
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
index 80de24868181..217092e6e4e5 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
@@ -24,7 +24,6 @@ import android.testing.AndroidTestingRunner;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.communal.CommunalStateController;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
-import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -61,8 +60,6 @@ public class KeyguardStatusViewControllerTest extends SysuiTestCase {
@Mock
KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
@Mock
- SmartspaceTransitionController mSmartSpaceTransitionController;
- @Mock
ScreenOffAnimationController mScreenOffAnimationController;
@Captor
private ArgumentCaptor<KeyguardUpdateMonitorCallback> mKeyguardUpdateMonitorCallbackCaptor;
@@ -83,7 +80,6 @@ public class KeyguardStatusViewControllerTest extends SysuiTestCase {
mConfigurationController,
mDozeParameters,
mKeyguardUnlockAnimationController,
- mSmartSpaceTransitionController,
mScreenOffAnimationController);
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 7266e41ad7ca..08d881ff96aa 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -88,7 +88,6 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
@@ -108,6 +107,7 @@ import org.mockito.MockitoAnnotations;
import org.mockito.MockitoSession;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -174,10 +174,10 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
private InteractionJankMonitor mInteractionJankMonitor;
@Mock
private LatencyTracker mLatencyTracker;
- @Mock
- private FeatureFlags mFeatureFlags;
@Captor
private ArgumentCaptor<StatusBarStateController.StateListener> mStatusBarStateListenerCaptor;
+ @Mock
+ private KeyguardUpdateMonitorCallback mTestCallback;
// Direct executor
private Executor mBackgroundExecutor = Runnable::run;
private Executor mMainExecutor = Runnable::run;
@@ -255,11 +255,13 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
verify(mStatusBarStateController).addCallback(mStatusBarStateListenerCaptor.capture());
mStatusBarStateListener = mStatusBarStateListenerCaptor.getValue();
+ mKeyguardUpdateMonitor.registerCallback(mTestCallback);
}
@After
public void tearDown() {
mMockitoSession.finishMocking();
+ mKeyguardUpdateMonitor.removeCallback(mTestCallback);
mKeyguardUpdateMonitor.destroy();
}
@@ -599,7 +601,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
mTestableLooper.processAllMessages();
when(mKeyguardBypassController.canBypass()).thenReturn(true);
mKeyguardUpdateMonitor.onTrustChanged(true /* enabled */,
- KeyguardUpdateMonitor.getCurrentUser(), 0 /* flags */);
+ KeyguardUpdateMonitor.getCurrentUser(), 0 /* flags */,
+ new ArrayList<>());
mKeyguardUpdateMonitor.onKeyguardVisibilityChanged(true);
verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt(), anyBoolean());
}
@@ -609,7 +612,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
mKeyguardUpdateMonitor.dispatchStartedWakingUp();
mTestableLooper.processAllMessages();
mKeyguardUpdateMonitor.onTrustChanged(true /* enabled */,
- KeyguardUpdateMonitor.getCurrentUser(), 0 /* flags */);
+ KeyguardUpdateMonitor.getCurrentUser(), 0 /* flags */, new ArrayList<>());
mKeyguardUpdateMonitor.onKeyguardVisibilityChanged(true);
verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt(),
anyBoolean());
@@ -754,7 +757,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
@Test
public void testGetUserCanSkipBouncer_whenTrust() {
int user = KeyguardUpdateMonitor.getCurrentUser();
- mKeyguardUpdateMonitor.onTrustChanged(true /* enabled */, user, 0 /* flags */);
+ mKeyguardUpdateMonitor.onTrustChanged(true /* enabled */, user, 0 /* flags */,
+ new ArrayList<>());
assertThat(mKeyguardUpdateMonitor.getUserCanSkipBouncer(user)).isTrue();
}
@@ -985,7 +989,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
// WHEN trust is enabled (ie: via smartlock)
mKeyguardUpdateMonitor.onTrustChanged(true /* enabled */,
- KeyguardUpdateMonitor.getCurrentUser(), 0 /* flags */);
+ KeyguardUpdateMonitor.getCurrentUser(), 0 /* flags */, new ArrayList<>());
// THEN we shouldn't listen for udfps
assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(true)).isEqualTo(false);
@@ -1069,6 +1073,17 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
anyBoolean());
}
+ @Test
+ public void testShowTrustGrantedMessage_onTrustGranted() {
+ // WHEN trust is enabled (ie: via some trust agent) with a trustGranted string
+ mKeyguardUpdateMonitor.onTrustChanged(true /* enabled */,
+ KeyguardUpdateMonitor.getCurrentUser(), 0 /* flags */,
+ Arrays.asList("Unlocked by wearable"));
+
+ // THEN the showTrustGrantedMessage should be called with the first message
+ verify(mTestCallback).showTrustGrantedMessage("Unlocked by wearable");
+ }
+
private void setKeyguardBouncerVisibility(boolean isVisible) {
mKeyguardUpdateMonitor.sendKeyguardBouncerChanged(isVisible);
mTestableLooper.processAllMessages();
@@ -1108,7 +1123,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
mRingerModeTracker, mBackgroundExecutor, mMainExecutor,
mStatusBarStateController, mLockPatternUtils,
mAuthController, mTelephonyListenerManager,
- mInteractionJankMonitor, mLatencyTracker, mFeatureFlags);
+ mInteractionJankMonitor, mLatencyTracker);
setStrongAuthTracker(KeyguardUpdateMonitorTest.this.mStrongAuthTracker);
}
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 6ddfbb2f430f..bc89da7d504c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
@@ -111,6 +111,7 @@ public class MagnificationModeSwitchTest extends SysuiTestCase {
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
+ mContext = Mockito.spy(getContext());
final WindowManager wm = mContext.getSystemService(WindowManager.class);
mSwitchListener = new SwitchListenerStub();
mWindowManager = spy(new TestableWindowManager(wm));
@@ -139,16 +140,18 @@ public class MagnificationModeSwitchTest extends SysuiTestCase {
public void tearDown() {
mFadeOutAnimation = null;
mMotionEventHelper.recycleEvents();
+ mMagnificationModeSwitch.removeButton();
}
@Test
- public void removeButton_buttonIsShowing_removeView() {
+ public void removeButton_buttonIsShowing_removeViewAndUnregisterComponentCallbacks() {
mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
mMagnificationModeSwitch.removeButton();
verify(mWindowManager).removeView(mSpyImageView);
verify(mViewPropertyAnimator).cancel();
+ verify(mContext).unregisterComponentCallbacks(mMagnificationModeSwitch);
}
@Test
@@ -464,6 +467,13 @@ public class MagnificationModeSwitchTest extends SysuiTestCase {
}
@Test
+ public void showButton_registerComponentCallbacks() {
+ mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
+
+ verify(mContext).registerComponentCallbacks(mMagnificationModeSwitch);
+ }
+
+ @Test
public void onLocaleChanged_buttonIsShowing_updateA11yWindowTitle() {
final String newA11yWindowTitle = "new a11y window title";
mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java
index 216f63fce885..a56218b08224 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java
@@ -91,7 +91,6 @@ public class ModeSwitchesControllerTest extends SysuiTestCase {
verify(mModeSwitch).onConfigurationChanged(ActivityInfo.CONFIG_DENSITY);
}
-
@Test
public void testOnSwitchClick_showWindowModeButton_invokeListener() {
mModeSwitchesController.showButton(Display.DEFAULT_DISPLAY,
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 5ad651728c66..1dd5e227a909 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
@@ -16,6 +16,8 @@
package com.android.systemui.accessibility;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.view.Choreographer.FrameCallback;
import static android.view.WindowInsets.Type.systemGestures;
import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
@@ -32,7 +34,6 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
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.atLeastOnce;
@@ -42,9 +43,11 @@ import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.animation.ValueAnimator;
import android.app.Instrumentation;
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.PointF;
@@ -52,6 +55,7 @@ import android.graphics.Rect;
import android.os.Handler;
import android.os.SystemClock;
import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
import android.testing.TestableResources;
import android.text.TextUtils;
import android.view.Display;
@@ -71,6 +75,7 @@ import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.model.SysUiState;
import com.android.systemui.util.leak.ReferenceTestUtils;
+import com.android.systemui.utils.os.FakeHandler;
import org.junit.After;
import org.junit.Before;
@@ -85,13 +90,12 @@ import org.mockito.MockitoAnnotations;
import java.util.List;
@LargeTest
+@TestableLooper.RunWithLooper
@RunWith(AndroidTestingRunner.class)
public class WindowMagnificationControllerTest extends SysuiTestCase {
private static final int LAYOUT_CHANGE_TIMEOUT_MS = 5000;
@Mock
- private Handler mHandler;
- @Mock
private SfVsyncFrameCallbackProvider mSfVsyncFrameProvider;
@Mock
private MirrorWindowControl mMirrorWindowControl;
@@ -99,17 +103,21 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
private WindowMagnifierCallback mWindowMagnifierCallback;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
+
+ private Handler mHandler;
private TestableWindowManager mWindowManager;
private SysUiState mSysUiState = new SysUiState();
private Resources mResources;
private WindowMagnificationAnimationController mWindowMagnificationAnimationController;
private WindowMagnificationController mWindowMagnificationController;
private Instrumentation mInstrumentation;
+ private final ValueAnimator mValueAnimator = ValueAnimator.ofFloat(0, 1.0f).setDuration(0);
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = Mockito.spy(getContext());
+ mHandler = new FakeHandler(TestableLooper.get(this).getLooper());
mInstrumentation = InstrumentationRegistry.getInstrumentation();
final WindowManager wm = mContext.getSystemService(WindowManager.class);
mWindowManager = spy(new TestableWindowManager(wm));
@@ -121,17 +129,11 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
return null;
}).when(mSfVsyncFrameProvider).postFrameCallback(
any(FrameCallback.class));
- doAnswer(invocation -> {
- final Runnable runnable = invocation.getArgument(0);
- runnable.run();
- return null;
- }).when(mHandler).post(
- any(Runnable.class));
mSysUiState.addCallback(Mockito.mock(SysUiState.SysUiStateCallback.class));
mResources = getContext().getOrCreateTestableResources().getResources();
mWindowMagnificationAnimationController = new WindowMagnificationAnimationController(
- mContext);
+ mContext, mValueAnimator);
mWindowMagnificationController = new WindowMagnificationController(mContext,
mHandler, mWindowMagnificationAnimationController, mSfVsyncFrameProvider,
mMirrorWindowControl, mTransaction, mWindowMagnifierCallback, mSysUiState);
@@ -144,6 +146,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
public void tearDown() {
mInstrumentation.runOnMainSync(
() -> mWindowMagnificationController.deleteWindowMagnification());
+ mValueAnimator.cancel();
}
@Test
@@ -223,13 +226,9 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
final int screenSize = mContext.getResources().getDimensionPixelSize(
R.dimen.magnification_max_frame_size) * 10;
mWindowManager.setWindowBounds(new Rect(0, 0, screenSize, screenSize));
- //We need to initialize new one because the window size is determined when initialization.
- final WindowMagnificationController controller = new WindowMagnificationController(mContext,
- mHandler, mWindowMagnificationAnimationController, mSfVsyncFrameProvider,
- mMirrorWindowControl, mTransaction, mWindowMagnifierCallback, mSysUiState);
mInstrumentation.runOnMainSync(() -> {
- controller.enableWindowMagnificationInternal(Float.NaN, Float.NaN,
+ mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN,
Float.NaN);
});
@@ -242,17 +241,17 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
}
@Test
- public void deleteWindowMagnification_destroyControl() {
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN,
- Float.NaN);
- });
+ public void deleteWindowMagnification_destroyControlAndUnregisterComponentCallback() {
+ mInstrumentation.runOnMainSync(
+ () -> mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN,
+ Float.NaN,
+ Float.NaN));
- mInstrumentation.runOnMainSync(() -> {
- mWindowMagnificationController.deleteWindowMagnification();
- });
+ mInstrumentation.runOnMainSync(
+ () -> mWindowMagnificationController.deleteWindowMagnification());
verify(mMirrorWindowControl).destroyControl();
+ verify(mContext).unregisterComponentCallbacks(mWindowMagnificationController);
}
@Test
@@ -288,12 +287,6 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
@Test
public void setScale_enabled_expectedValueAndUpdateStateDescription() {
- doAnswer(invocation -> {
- final Runnable runnable = invocation.getArgument(0);
- runnable.run();
- return null;
- }).when(mHandler).postDelayed(any(Runnable.class), anyLong());
-
mInstrumentation.runOnMainSync(
() -> mWindowMagnificationController.enableWindowMagnificationInternal(2.0f,
Float.NaN, Float.NaN));
@@ -322,11 +315,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
@Test
public void onOrientationChanged_enabled_updateDisplayRotationAndCenterStayAtSamePosition() {
- final Display display = Mockito.spy(mContext.getDisplay());
- final int currentRotation = display.getRotation();
- final int newRotation = (currentRotation + 1) % 4;
- when(display.getRotation()).thenReturn(newRotation);
- when(mContext.getDisplay()).thenReturn(display);
+ final int newRotation = simulateRotateTheDevice();
final Rect windowBounds = new Rect(mWindowManager.getCurrentWindowMetrics().getBounds());
final float center = Math.min(windowBounds.exactCenterX(), windowBounds.exactCenterY());
final float displayWidth = windowBounds.width();
@@ -535,6 +524,30 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
}
@Test
+ public void enableWindowMagnification_rotationIsChanged_updateRotationValue() {
+ final Configuration config = mContext.getResources().getConfiguration();
+ config.orientation = config.orientation == ORIENTATION_LANDSCAPE ? ORIENTATION_PORTRAIT
+ : ORIENTATION_LANDSCAPE;
+ final int newRotation = simulateRotateTheDevice();
+
+ mInstrumentation.runOnMainSync(
+ () -> mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN,
+ Float.NaN, Float.NaN));
+
+ assertEquals(newRotation, mWindowMagnificationController.mRotation);
+ }
+
+ @Test
+ public void enableWindowMagnification_registerComponentCallback() {
+ mInstrumentation.runOnMainSync(
+ () -> mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN,
+ Float.NaN,
+ Float.NaN));
+
+ verify(mContext).registerComponentCallbacks(mWindowMagnificationController);
+ }
+
+ @Test
public void onLocaleChanged_enabled_updateA11yWindowTitle() {
final String newA11yWindowTitle = "new a11y window title";
mInstrumentation.runOnMainSync(() -> {
@@ -610,4 +623,14 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
.build();
mWindowManager.setWindowInsets(testInsets);
}
+
+ @Surface.Rotation
+ private int simulateRotateTheDevice() {
+ final Display display = Mockito.spy(mContext.getDisplay());
+ final int currentRotation = display.getRotation();
+ final int newRotation = (currentRotation + 1) % 4;
+ when(display.getRotation()).thenReturn(newRotation);
+ when(mContext.getDisplay()).thenReturn(display);
+ return newRotation;
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java
index 343658d31272..d3f30c508b8b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java
@@ -30,7 +30,6 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
-import android.content.res.Configuration;
import android.graphics.Rect;
import android.hardware.display.DisplayManager;
import android.os.RemoteException;
@@ -159,15 +158,6 @@ public class WindowMagnificationTest extends SysuiTestCase {
}
@Test
- public void onConfigurationChanged_updateModeSwitches() {
- final Configuration config = new Configuration();
- config.densityDpi = Configuration.DENSITY_DPI_ANY;
- mWindowMagnification.onConfigurationChanged(config);
-
- verify(mModeSwitchesController).onConfigurationChanged(anyInt());
- }
-
- @Test
public void overviewProxyIsConnected_noController_resetFlag() {
mOverviewProxyListener.onConnectionChanged(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java
index 3e19cc436dca..cdffaecadd77 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java
@@ -192,7 +192,7 @@ public class DozeScreenStateTest extends SysuiTestCase {
public void test_holdsWakeLockWhenGoingToLowPowerDelayed() {
// Transition to low power mode will be delayed to let
// animations play at 60 fps.
- when(mDozeParameters.shouldControlScreenOff()).thenReturn(true);
+ when(mDozeParameters.shouldDelayDisplayDozeTransition()).thenReturn(true);
mHandlerFake.setMode(QUEUEING);
mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
@@ -209,7 +209,7 @@ public class DozeScreenStateTest extends SysuiTestCase {
public void test_releasesWakeLock_abortingLowPowerDelayed() {
// Transition to low power mode will be delayed to let
// animations play at 60 fps.
- when(mDozeParameters.shouldControlScreenOff()).thenReturn(true);
+ when(mDozeParameters.shouldDelayDisplayDozeTransition()).thenReturn(true);
mHandlerFake.setMode(QUEUEING);
mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/ComplicationProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/ComplicationProviderTest.java
new file mode 100644
index 000000000000..ada7ddbdb287
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/ComplicationProviderTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.dreams;
+
+import static org.junit.Assert.assertEquals;
+
+import android.content.Context;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.settingslib.dream.DreamBackend;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class ComplicationProviderTest {
+ private TestComplicationProvider mComplicationProvider;
+
+ @Before
+ public void setup() {
+ mComplicationProvider = new TestComplicationProvider();
+ }
+
+ @Test
+ public void testConvertComplicationType() {
+ assertEquals(ComplicationProvider.COMPLICATION_TYPE_TIME,
+ mComplicationProvider.convertComplicationType(DreamBackend.COMPLICATION_TYPE_TIME));
+ assertEquals(ComplicationProvider.COMPLICATION_TYPE_DATE,
+ mComplicationProvider.convertComplicationType(DreamBackend.COMPLICATION_TYPE_DATE));
+ assertEquals(ComplicationProvider.COMPLICATION_TYPE_WEATHER,
+ mComplicationProvider.convertComplicationType(
+ DreamBackend.COMPLICATION_TYPE_WEATHER));
+ assertEquals(ComplicationProvider.COMPLICATION_TYPE_AIR_QUALITY,
+ mComplicationProvider.convertComplicationType(
+ DreamBackend.COMPLICATION_TYPE_AIR_QUALITY));
+ assertEquals(ComplicationProvider.COMPLICATION_TYPE_CAST_INFO,
+ mComplicationProvider.convertComplicationType(
+ DreamBackend.COMPLICATION_TYPE_CAST_INFO));
+ }
+
+ private static class TestComplicationProvider implements ComplicationProvider {
+ @Override
+ public void onCreateComplication(Context context,
+ ComplicationHost.CreationCallback creationCallback,
+ ComplicationHost.InteractionCallback interactionCallback) {
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/appwidgets/ComplicationPrimerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/appwidgets/ComplicationPrimerTest.java
deleted file mode 100644
index adf110bb2494..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/appwidgets/ComplicationPrimerTest.java
+++ /dev/null
@@ -1,191 +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.dreams.appwidgets;
-
-import static org.junit.Assert.assertEquals;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.ComponentName;
-import android.content.res.Resources;
-import android.testing.AndroidTestingRunner;
-import android.view.Gravity;
-
-import androidx.constraintlayout.widget.ConstraintLayout;
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.R;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.SysuiTestableContext;
-import com.android.systemui.dreams.DreamOverlayStateController;
-import com.android.systemui.dreams.appwidgets.dagger.AppWidgetComponent;
-import com.android.systemui.utils.leaks.LeakCheckedTest;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-public class ComplicationPrimerTest extends SysuiTestCase {
- @Rule
- public final LeakCheckedTest.SysuiLeakCheck mLeakCheck = new LeakCheckedTest.SysuiLeakCheck();
-
- @Rule
- public SysuiTestableContext mContext = new SysuiTestableContext(
- InstrumentationRegistry.getContext(), mLeakCheck);
-
- @Mock
- Resources mResources;
-
- @Mock
- AppWidgetComponent mAppWidgetComplicationComponent1;
- @Mock
- AppWidgetComponent mAppWidgetComplicationComponent2;
-
- @Mock
- ComplicationProvider mComplicationProvider1;
-
- @Mock
- ComplicationProvider mComplicationProvider2;
-
- final ComponentName mAppComplicationComponent1 =
- ComponentName.unflattenFromString("com.foo.bar/.Baz");
- final ComponentName mAppComplicationComponent2 =
- ComponentName.unflattenFromString("com.foo.bar/.Baz2");
-
- final int mAppComplicationGravity1 = Gravity.BOTTOM | Gravity.START;
- final int mAppComplicationGravity2 = Gravity.BOTTOM | Gravity.END;
-
- final String[] mComponents = new String[]{mAppComplicationComponent1.flattenToString(),
- mAppComplicationComponent2.flattenToString() };
- final int[] mPositions = new int[]{mAppComplicationGravity1, mAppComplicationGravity2};
-
- @Mock
- DreamOverlayStateController mDreamOverlayStateController;
-
- @Mock
- AppWidgetComponent.Factory mAppWidgetComplicationProviderFactory;
-
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
- when(mAppWidgetComplicationProviderFactory.build(eq(mAppComplicationComponent1), any()))
- .thenReturn(mAppWidgetComplicationComponent1);
- when(mAppWidgetComplicationComponent1.getAppWidgetComplicationProvider())
- .thenReturn(mComplicationProvider1);
- when(mAppWidgetComplicationProviderFactory.build(eq(mAppComplicationComponent2), any()))
- .thenReturn(mAppWidgetComplicationComponent2);
- when(mAppWidgetComplicationComponent2.getAppWidgetComplicationProvider())
- .thenReturn(mComplicationProvider2);
- when(mResources.getIntArray(R.array.config_dreamComplicationPositions))
- .thenReturn(mPositions);
- when(mResources.getStringArray(R.array.config_dreamAppWidgetComplications))
- .thenReturn(mComponents);
- }
-
- @Test
- public void testLoading() {
- final ComplicationPrimer primer = new ComplicationPrimer(mContext,
- mResources,
- mDreamOverlayStateController,
- mAppWidgetComplicationProviderFactory);
-
- // Inform primer to begin.
- primer.onBootCompleted();
-
- // Verify the first component is added to the state controller with the proper position.
- {
- final ArgumentCaptor<ConstraintLayout.LayoutParams> layoutParamsArgumentCaptor =
- ArgumentCaptor.forClass(ConstraintLayout.LayoutParams.class);
- verify(mAppWidgetComplicationProviderFactory, times(1))
- .build(eq(mAppComplicationComponent1),
- layoutParamsArgumentCaptor.capture());
-
- assertEquals(layoutParamsArgumentCaptor.getValue().startToStart,
- ConstraintLayout.LayoutParams.PARENT_ID);
- assertEquals(layoutParamsArgumentCaptor.getValue().bottomToBottom,
- ConstraintLayout.LayoutParams.PARENT_ID);
-
- verify(mDreamOverlayStateController, times(1))
- .addComplication(eq(mComplicationProvider1));
- }
-
- // Verify the second component is added to the state controller with the proper position.
- {
- final ArgumentCaptor<ConstraintLayout.LayoutParams> layoutParamsArgumentCaptor =
- ArgumentCaptor.forClass(ConstraintLayout.LayoutParams.class);
- verify(mAppWidgetComplicationProviderFactory, times(1))
- .build(eq(mAppComplicationComponent2),
- layoutParamsArgumentCaptor.capture());
-
- assertEquals(layoutParamsArgumentCaptor.getValue().endToEnd,
- ConstraintLayout.LayoutParams.PARENT_ID);
- assertEquals(layoutParamsArgumentCaptor.getValue().bottomToBottom,
- ConstraintLayout.LayoutParams.PARENT_ID);
- verify(mDreamOverlayStateController, times(1))
- .addComplication(eq(mComplicationProvider1));
- }
- }
-
- @Test
- public void testNoComponents() {
- when(mResources.getStringArray(R.array.config_dreamAppWidgetComplications))
- .thenReturn(new String[]{});
-
- final ComplicationPrimer primer = new ComplicationPrimer(mContext,
- mResources,
- mDreamOverlayStateController,
- mAppWidgetComplicationProviderFactory);
-
- // Inform primer to begin.
- primer.onBootCompleted();
-
-
- // Make sure there is no request to add a widget if no components are specified by the
- // product.
- verify(mAppWidgetComplicationProviderFactory, never()).build(any(), any());
- verify(mDreamOverlayStateController, never()).addComplication(any());
- }
-
- @Test
- public void testNoPositions() {
- when(mResources.getIntArray(R.array.config_dreamComplicationPositions))
- .thenReturn(new int[]{});
-
- final ComplicationPrimer primer = new ComplicationPrimer(mContext,
- mResources,
- mDreamOverlayStateController,
- mAppWidgetComplicationProviderFactory);
-
- primer.onBootCompleted();
-
- // Make sure there is no request to add a widget if no positions are specified by the
- // product.
- verify(mAppWidgetComplicationProviderFactory, never()).build(any(), any());
- verify(mDreamOverlayStateController, never()).addComplication(any());
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/appwidgets/ComplicationProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/appwidgets/ComplicationProviderTest.java
deleted file mode 100644
index f538112edbda..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/appwidgets/ComplicationProviderTest.java
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.dreams.appwidgets;
-
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.isNull;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.app.PendingIntent;
-import android.appwidget.AppWidgetHostView;
-import android.content.ComponentName;
-import android.testing.AndroidTestingRunner;
-import android.widget.RemoteViews;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.SysuiTestableContext;
-import com.android.systemui.dreams.ComplicationHost;
-import com.android.systemui.dreams.ComplicationHostView;
-import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.utils.leaks.LeakCheckedTest;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-public class ComplicationProviderTest extends SysuiTestCase {
- @Mock
- ActivityStarter mActivityStarter;
-
- @Mock
- ComponentName mComponentName;
-
- @Mock
- AppWidgetProvider mAppWidgetProvider;
-
- @Mock
- AppWidgetHostView mAppWidgetHostView;
-
- @Mock
- ComplicationHost.CreationCallback mCreationCallback;
-
- @Mock
- ComplicationHost.InteractionCallback mInteractionCallback;
-
- @Mock
- PendingIntent mPendingIntent;
-
- @Mock
- RemoteViews.RemoteResponse mRemoteResponse;
-
- ComplicationProvider mComplicationProvider;
-
- RemoteViews.InteractionHandler mInteractionHandler;
-
- @Rule
- public final LeakCheckedTest.SysuiLeakCheck mLeakCheck = new LeakCheckedTest.SysuiLeakCheck();
-
- @Rule
- public SysuiTestableContext mContext = new SysuiTestableContext(
- InstrumentationRegistry.getContext(), mLeakCheck);
-
- ComplicationHostView.LayoutParams mLayoutParams = new ComplicationHostView.LayoutParams(
- ComplicationHostView.LayoutParams.MATCH_PARENT,
- ComplicationHostView.LayoutParams.MATCH_PARENT);
-
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
- when(mPendingIntent.isActivity()).thenReturn(true);
- when(mAppWidgetProvider.getWidget(mComponentName)).thenReturn(mAppWidgetHostView);
-
- mComplicationProvider = new ComplicationProvider(
- mActivityStarter,
- mComponentName,
- mAppWidgetProvider,
- mLayoutParams
- );
-
- final ArgumentCaptor<RemoteViews.InteractionHandler> creationCallbackCapture =
- ArgumentCaptor.forClass(RemoteViews.InteractionHandler.class);
-
- mComplicationProvider.onCreateComplication(mContext, mCreationCallback,
- mInteractionCallback);
- verify(mAppWidgetHostView, times(1))
- .setInteractionHandler(creationCallbackCapture.capture());
- mInteractionHandler = creationCallbackCapture.getValue();
- }
-
- @Test
- public void testWidgetBringup() {
- // Make sure widget was requested.
- verify(mAppWidgetProvider, times(1)).getWidget(eq(mComponentName));
-
- // Make sure widget was returned to callback.
- verify(mCreationCallback, times(1)).onCreated(eq(mAppWidgetHostView),
- eq(mLayoutParams));
- }
-
- @Test
- public void testWidgetInteraction() {
- // Trigger interaction.
- mInteractionHandler.onInteraction(mAppWidgetHostView, mPendingIntent,
- mRemoteResponse);
-
- // Ensure activity is started.
- verify(mActivityStarter, times(1))
- .startPendingIntentDismissingKeyguard(eq(mPendingIntent), isNull(),
- eq(mAppWidgetHostView));
- // Verify exit is requested.
- verify(mInteractionCallback, times(1)).onExit();
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
index f3043e934c8a..fb1a968acceb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
@@ -14,7 +14,6 @@ import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardViewController
import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController
import com.android.systemui.statusbar.phone.BiometricUnlockController
import com.android.systemui.statusbar.policy.KeyguardStateController
import junit.framework.Assert.assertEquals
@@ -44,8 +43,6 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {
@Mock
private lateinit var keyguardViewController: KeyguardViewController
@Mock
- private lateinit var smartspaceTransitionController: SmartspaceTransitionController
- @Mock
private lateinit var featureFlags: FeatureFlags
@Mock
private lateinit var biometricUnlockController: BiometricUnlockController
@@ -59,7 +56,7 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {
MockitoAnnotations.initMocks(this)
keyguardUnlockAnimationController = KeyguardUnlockAnimationController(
context, keyguardStateController, { keyguardViewMediator }, keyguardViewController,
- smartspaceTransitionController, featureFlags, biometricUnlockController
+ featureFlags, { biometricUnlockController }
)
`when`(keyguardViewController.viewRootImpl).thenReturn(mock(ViewRootImpl::class.java))
@@ -87,7 +84,7 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {
fun noSurfaceAnimation_ifWakeAndUnlocking() {
`when`(biometricUnlockController.isWakeAndUnlock).thenReturn(true)
- keyguardUnlockAnimationController.notifyStartKeyguardExitAnimation(
+ keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
remoteAnimationTarget,
0 /* startTime */,
false /* requestedShowSurfaceBehindKeyguard */
@@ -118,15 +115,12 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {
fun surfaceAnimation_ifNotWakeAndUnlocking() {
`when`(biometricUnlockController.isWakeAndUnlock).thenReturn(false)
- keyguardUnlockAnimationController.notifyStartKeyguardExitAnimation(
+ keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
remoteAnimationTarget,
0 /* startTime */,
false /* requestedShowSurfaceBehindKeyguard */
)
- // Make sure the animator was started.
- assertTrue(keyguardUnlockAnimationController.surfaceBehindEntryAnimator.isRunning)
-
// Since the animation is running, we should not have finished the remote animation.
verify(keyguardViewMediator, times(0)).onKeyguardExitRemoteAnimationFinished(
false /* cancelled */)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt
index 4839bdea1b70..a1ec38f630ed 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt
@@ -21,16 +21,14 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.media.taptotransfer.receiver.ChipStateReceiver
import com.android.systemui.media.taptotransfer.receiver.MediaTttChipControllerReceiver
-import com.android.systemui.media.taptotransfer.sender.*
+import com.android.systemui.media.taptotransfer.sender.MediaTttSenderService
import com.android.systemui.shared.mediattt.DeviceInfo
-import com.android.systemui.shared.mediattt.IDeviceSenderCallback
+import com.android.systemui.shared.mediattt.IDeviceSenderService
import com.android.systemui.statusbar.commandline.Command
import com.android.systemui.statusbar.commandline.CommandRegistry
-import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.capture
-import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
@@ -53,11 +51,9 @@ class MediaTttCommandLineHelperTest : SysuiTestCase() {
private lateinit var mediaTttCommandLineHelper: MediaTttCommandLineHelper
@Mock
- private lateinit var mediaTttChipControllerSender: MediaTttChipControllerSender
- @Mock
private lateinit var mediaTttChipControllerReceiver: MediaTttChipControllerReceiver
@Mock
- private lateinit var mediaSenderService: IDeviceSenderCallback.Stub
+ private lateinit var mediaSenderService: IDeviceSenderService.Stub
private lateinit var mediaSenderServiceComponentName: ComponentName
@Before
@@ -73,28 +69,15 @@ class MediaTttCommandLineHelperTest : SysuiTestCase() {
MediaTttCommandLineHelper(
commandRegistry,
context,
- mediaTttChipControllerSender,
mediaTttChipControllerReceiver,
- FakeExecutor(FakeSystemClock())
)
}
@Test(expected = IllegalStateException::class)
- fun constructor_addSenderCommandAlreadyRegistered() {
- // Since creating the chip controller should automatically register the add command, it
+ fun constructor_senderCommandAlreadyRegistered() {
+ // Since creating the chip controller should automatically register the sender command, it
// should throw when registering it again.
- commandRegistry.registerCommand(
- ADD_CHIP_COMMAND_SENDER_TAG
- ) { EmptyCommand() }
- }
-
- @Test(expected = IllegalStateException::class)
- fun constructor_removeSenderCommandAlreadyRegistered() {
- // Since creating the chip controller should automatically register the remove command, it
- // should throw when registering it again.
- commandRegistry.registerCommand(
- REMOVE_CHIP_COMMAND_SENDER_TAG
- ) { EmptyCommand() }
+ commandRegistry.registerCommand(SENDER_COMMAND) { EmptyCommand() }
}
@Test(expected = IllegalStateException::class)
@@ -127,24 +110,74 @@ class MediaTttCommandLineHelperTest : SysuiTestCase() {
}
@Test
- fun sender_transferInitiated_chipDisplayWithCorrectState() {
- commandRegistry.onShellCommand(pw, getTransferInitiatedCommand())
+ fun sender_moveCloserToEndCast_serviceCallbackCalled() {
+ commandRegistry.onShellCommand(pw, getMoveCloserToEndCastCommand())
+
+ assertThat(context.isBound(mediaSenderServiceComponentName)).isTrue()
- verify(mediaTttChipControllerSender).displayChip(any(TransferInitiated::class.java))
+ val deviceInfoCaptor = argumentCaptor<DeviceInfo>()
+ verify(mediaSenderService).closeToReceiverToEndCast(any(), capture(deviceInfoCaptor))
+ assertThat(deviceInfoCaptor.value!!.name).isEqualTo(DEVICE_NAME)
}
@Test
- fun sender_transferSucceeded_chipDisplayWithCorrectState() {
- commandRegistry.onShellCommand(pw, getTransferSucceededCommand())
+ fun sender_transferToReceiverTriggered_chipDisplayWithCorrectState() {
+ commandRegistry.onShellCommand(pw, getTransferToReceiverTriggeredCommand())
+
+ assertThat(context.isBound(mediaSenderServiceComponentName)).isTrue()
- verify(mediaTttChipControllerSender).displayChip(any(TransferSucceeded::class.java))
+ val deviceInfoCaptor = argumentCaptor<DeviceInfo>()
+ verify(mediaSenderService).transferToReceiverTriggered(any(), capture(deviceInfoCaptor))
+ assertThat(deviceInfoCaptor.value!!.name).isEqualTo(DEVICE_NAME)
+ }
+
+ @Test
+ fun sender_transferToThisDeviceTriggered_chipDisplayWithCorrectState() {
+ commandRegistry.onShellCommand(pw, getTransferToThisDeviceTriggeredCommand())
+
+ assertThat(context.isBound(mediaSenderServiceComponentName)).isTrue()
+ verify(mediaSenderService).transferToThisDeviceTriggered(any(), any())
}
@Test
- fun sender_removeCommand_chipRemoved() {
- commandRegistry.onShellCommand(pw, arrayOf(REMOVE_CHIP_COMMAND_SENDER_TAG))
+ fun sender_transferToReceiverSucceeded_chipDisplayWithCorrectState() {
+ commandRegistry.onShellCommand(pw, getTransferToReceiverSucceededCommand())
+
+ assertThat(context.isBound(mediaSenderServiceComponentName)).isTrue()
- verify(mediaTttChipControllerSender).removeChip()
+ val deviceInfoCaptor = argumentCaptor<DeviceInfo>()
+ verify(mediaSenderService)
+ .transferToReceiverSucceeded(any(), capture(deviceInfoCaptor), any())
+ assertThat(deviceInfoCaptor.value!!.name).isEqualTo(DEVICE_NAME)
+ }
+
+ @Test
+ fun sender_transferToThisDeviceSucceeded_chipDisplayWithCorrectState() {
+ commandRegistry.onShellCommand(pw, getTransferToThisDeviceSucceededCommand())
+
+ assertThat(context.isBound(mediaSenderServiceComponentName)).isTrue()
+
+ val deviceInfoCaptor = argumentCaptor<DeviceInfo>()
+ verify(mediaSenderService)
+ .transferToThisDeviceSucceeded(any(), capture(deviceInfoCaptor), any())
+ assertThat(deviceInfoCaptor.value!!.name).isEqualTo(DEVICE_NAME)
+ }
+
+ @Test
+ fun sender_transferFailed_serviceCallbackCalled() {
+ commandRegistry.onShellCommand(pw, getTransferFailedCommand())
+
+ assertThat(context.isBound(mediaSenderServiceComponentName)).isTrue()
+ verify(mediaSenderService).transferFailed(any(), any())
+ }
+
+ @Test
+ fun sender_noLongerCloseToReceiver_serviceCallbackCalledAndServiceUnbound() {
+ commandRegistry.onShellCommand(pw, getNoLongerCloseToReceiverCommand())
+
+ // Once we're no longer close to the receiver, we should unbind the service.
+ assertThat(context.isBound(mediaSenderServiceComponentName)).isFalse()
+ verify(mediaSenderService).noLongerCloseToReceiver(any(), any())
}
@Test
@@ -163,23 +196,58 @@ class MediaTttCommandLineHelperTest : SysuiTestCase() {
private fun getMoveCloserToStartCastCommand(): Array<String> =
arrayOf(
- ADD_CHIP_COMMAND_SENDER_TAG,
+ SENDER_COMMAND,
DEVICE_NAME,
MOVE_CLOSER_TO_START_CAST_COMMAND_NAME
)
- private fun getTransferInitiatedCommand(): Array<String> =
+ private fun getMoveCloserToEndCastCommand(): Array<String> =
+ arrayOf(
+ SENDER_COMMAND,
+ DEVICE_NAME,
+ MOVE_CLOSER_TO_END_CAST_COMMAND_NAME
+ )
+
+ private fun getTransferToReceiverTriggeredCommand(): Array<String> =
+ arrayOf(
+ SENDER_COMMAND,
+ DEVICE_NAME,
+ TRANSFER_TO_RECEIVER_TRIGGERED_COMMAND_NAME
+ )
+
+ private fun getTransferToThisDeviceTriggeredCommand(): Array<String> =
+ arrayOf(
+ SENDER_COMMAND,
+ DEVICE_NAME,
+ TRANSFER_TO_THIS_DEVICE_TRIGGERED_COMMAND_NAME
+ )
+
+ private fun getTransferToReceiverSucceededCommand(): Array<String> =
+ arrayOf(
+ SENDER_COMMAND,
+ DEVICE_NAME,
+ TRANSFER_TO_RECEIVER_SUCCEEDED_COMMAND_NAME
+ )
+
+ private fun getTransferToThisDeviceSucceededCommand(): Array<String> =
+ arrayOf(
+ SENDER_COMMAND,
+ DEVICE_NAME,
+ TRANSFER_TO_THIS_DEVICE_SUCCEEDED_COMMAND_NAME
+ )
+
+ private fun getTransferFailedCommand(): Array<String> =
arrayOf(
- ADD_CHIP_COMMAND_SENDER_TAG,
+ SENDER_COMMAND,
DEVICE_NAME,
- TRANSFER_INITIATED_COMMAND_NAME
+ TRANSFER_FAILED_COMMAND_NAME
)
- private fun getTransferSucceededCommand(): Array<String> =
+ private fun getNoLongerCloseToReceiverCommand(): Array<String> =
arrayOf(
- ADD_CHIP_COMMAND_SENDER_TAG,
+ SENDER_COMMAND,
DEVICE_NAME,
- TRANSFER_SUCCEEDED_COMMAND_NAME
+ NO_LONGER_CLOSE_TO_RECEIVER_COMMAND_NAME
)
class EmptyCommand : Command {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
index ecc4c46634b9..509ae337abb8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
@@ -26,26 +26,19 @@ import android.widget.TextView
import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
-import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.shared.mediattt.IUndoTransferCallback
import com.android.systemui.util.mockito.any
-import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
-import com.google.common.util.concurrent.SettableFuture
import org.junit.Before
import org.junit.Test
import org.mockito.ArgumentCaptor
import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
-import java.util.concurrent.Future
@SmallTest
class MediaTttChipControllerSenderTest : SysuiTestCase() {
private lateinit var appIconDrawable: Drawable
- private lateinit var fakeMainClock: FakeSystemClock
- private lateinit var fakeMainExecutor: FakeExecutor
- private lateinit var fakeBackgroundClock: FakeSystemClock
- private lateinit var fakeBackgroundExecutor: FakeExecutor
private lateinit var controllerSender: MediaTttChipControllerSender
@@ -56,124 +49,153 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
fun setUp() {
MockitoAnnotations.initMocks(this)
appIconDrawable = Icon.createWithResource(context, R.drawable.ic_cake).loadDrawable(context)
- fakeMainClock = FakeSystemClock()
- fakeMainExecutor = FakeExecutor(fakeMainClock)
- fakeBackgroundClock = FakeSystemClock()
- fakeBackgroundExecutor = FakeExecutor(fakeBackgroundClock)
- controllerSender = MediaTttChipControllerSender(
- context, windowManager, fakeMainExecutor, fakeBackgroundExecutor
- )
+ controllerSender = MediaTttChipControllerSender(context, windowManager)
}
@Test
- fun moveCloserToStartCast_appIcon_chipTextContainsDeviceName_noLoadingIcon_noUndo() {
- controllerSender.displayChip(moveCloserToStartCast())
+ fun moveCloserToStartCast_appIcon_deviceName_noLoadingIcon_noUndo_noFailureIcon() {
+ val state = moveCloserToStartCast()
+ controllerSender.displayChip(state)
val chipView = getChipView()
assertThat(chipView.getAppIconView().drawable).isEqualTo(appIconDrawable)
assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_ICON_CONTENT_DESC)
- assertThat(chipView.getChipText()).contains(DEVICE_NAME)
+ assertThat(chipView.getChipText()).isEqualTo(state.getChipTextString(context))
assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE)
assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
+ assertThat(chipView.getFailureIcon().visibility).isEqualTo(View.GONE)
}
@Test
- fun transferInitiated_futureNotResolvedYet_appIcon_loadingIcon_noUndo() {
- val future: SettableFuture<Runnable?> = SettableFuture.create()
- controllerSender.displayChip(transferInitiated(future))
+ fun moveCloserToEndCast_appIcon_deviceName_noLoadingIcon_noUndo_noFailureIcon() {
+ val state = moveCloserToEndCast()
+ controllerSender.displayChip(state)
- // Don't resolve the future in any way and don't run our executors
+ val chipView = getChipView()
+ assertThat(chipView.getAppIconView().drawable).isEqualTo(appIconDrawable)
+ assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_ICON_CONTENT_DESC)
+ assertThat(chipView.getChipText()).isEqualTo(state.getChipTextString(context))
+ assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE)
+ assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
+ assertThat(chipView.getFailureIcon().visibility).isEqualTo(View.GONE)
+ }
+
+ @Test
+ fun transferToReceiverTriggered_appIcon_loadingIcon_noUndo_noFailureIcon() {
+ val state = transferToReceiverTriggered()
+ controllerSender.displayChip(state)
- // Assert we're still in the loading state
val chipView = getChipView()
assertThat(chipView.getAppIconView().drawable).isEqualTo(appIconDrawable)
assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_ICON_CONTENT_DESC)
- assertThat(chipView.getChipText()).contains(DEVICE_NAME)
+ assertThat(chipView.getChipText()).isEqualTo(state.getChipTextString(context))
assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.VISIBLE)
assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
+ assertThat(chipView.getFailureIcon().visibility).isEqualTo(View.GONE)
}
@Test
- fun transferInitiated_futureResolvedSuccessfully_switchesToTransferSucceeded() {
- val future: SettableFuture<Runnable?> = SettableFuture.create()
- val undoRunnable = Runnable { }
+ fun transferToThisDeviceTriggered_appIcon_loadingIcon_noUndo_noFailureIcon() {
+ val state = transferToThisDeviceTriggered()
+ controllerSender.displayChip(state)
- controllerSender.displayChip(transferInitiated(future))
+ val chipView = getChipView()
+ assertThat(chipView.getAppIconView().drawable).isEqualTo(appIconDrawable)
+ assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_ICON_CONTENT_DESC)
+ assertThat(chipView.getChipText()).isEqualTo(state.getChipTextString(context))
+ assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.VISIBLE)
+ assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
+ assertThat(chipView.getFailureIcon().visibility).isEqualTo(View.GONE)
+ }
- future.set(undoRunnable)
- fakeBackgroundExecutor.advanceClockToLast()
- fakeBackgroundExecutor.runAllReady()
- fakeMainExecutor.advanceClockToLast()
- val numRun = fakeMainExecutor.runAllReady()
+ @Test
+ fun transferToReceiverSucceeded_appIcon_deviceName_noLoadingIcon_noFailureIcon() {
+ val state = transferToReceiverSucceeded()
+ controllerSender.displayChip(state)
- // Assert we ran the future callback
- assertThat(numRun).isEqualTo(1)
- // Assert that we've moved to the successful state
val chipView = getChipView()
- assertThat(chipView.getChipText()).contains(DEVICE_NAME)
+ assertThat(chipView.getAppIconView().drawable).isEqualTo(appIconDrawable)
+ assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_ICON_CONTENT_DESC)
+ assertThat(chipView.getChipText()).isEqualTo(state.getChipTextString(context))
assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE)
- assertThat(chipView.getUndoButton().visibility).isEqualTo(View.VISIBLE)
+ assertThat(chipView.getFailureIcon().visibility).isEqualTo(View.GONE)
}
@Test
- fun transferInitiated_futureCancelled_chipRemoved() {
- val future: SettableFuture<Runnable?> = SettableFuture.create()
+ fun transferToReceiverSucceeded_nullUndoRunnable_noUndo() {
+ controllerSender.displayChip(transferToReceiverSucceeded(undoCallback = null))
- controllerSender.displayChip(transferInitiated(future))
+ val chipView = getChipView()
+ assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
+ }
- future.cancel(true)
- fakeBackgroundExecutor.advanceClockToLast()
- fakeBackgroundExecutor.runAllReady()
- fakeMainExecutor.advanceClockToLast()
- val numRun = fakeMainExecutor.runAllReady()
+ @Test
+ fun transferToReceiverSucceeded_withUndoRunnable_undoWithClick() {
+ val undoCallback = object : IUndoTransferCallback.Stub() {
+ override fun onUndoTriggered() {}
+ }
+ controllerSender.displayChip(transferToReceiverSucceeded(undoCallback))
- // Assert we ran the future callback
- assertThat(numRun).isEqualTo(1)
- // Assert that we've hidden the chip
- verify(windowManager).removeView(any())
+ val chipView = getChipView()
+ assertThat(chipView.getUndoButton().visibility).isEqualTo(View.VISIBLE)
+ assertThat(chipView.getUndoButton().hasOnClickListeners()).isTrue()
}
@Test
- fun transferInitiated_futureNotResolvedAfterTimeout_chipRemoved() {
- val future: SettableFuture<Runnable?> = SettableFuture.create()
- controllerSender.displayChip(transferInitiated(future))
-
- // We won't set anything on the future, but we will still run the executors so that we're
- // waiting on the future resolving. If we have a bug in our code, then this test will time
- // out because we're waiting on the future indefinitely.
- fakeBackgroundExecutor.advanceClockToLast()
- fakeBackgroundExecutor.runAllReady()
- fakeMainExecutor.advanceClockToLast()
- val numRun = fakeMainExecutor.runAllReady()
-
- // Assert we eventually decide to not wait for the future anymore
- assertThat(numRun).isEqualTo(1)
- // Assert we've hidden the chip
- verify(windowManager).removeView(any())
+ fun transferToReceiverSucceeded_withUndoRunnable_undoButtonClickRunsRunnable() {
+ var undoCallbackCalled = false
+ val undoCallback = object : IUndoTransferCallback.Stub() {
+ override fun onUndoTriggered() {
+ undoCallbackCalled = true
+ }
+ }
+
+ controllerSender.displayChip(transferToReceiverSucceeded(undoCallback))
+ getChipView().getUndoButton().performClick()
+
+ assertThat(undoCallbackCalled).isTrue()
}
@Test
- fun transferSucceeded_appIcon_chipTextContainsDeviceName_noLoadingIcon() {
- controllerSender.displayChip(transferSucceeded())
+ fun transferToReceiverSucceeded_undoButtonClick_switchesToTransferToThisDeviceTriggered() {
+ val undoCallback = object : IUndoTransferCallback.Stub() {
+ override fun onUndoTriggered() {}
+ }
+ controllerSender.displayChip(transferToReceiverSucceeded(undoCallback))
+
+ getChipView().getUndoButton().performClick()
+
+ assertThat(getChipView().getChipText())
+ .isEqualTo(transferToThisDeviceTriggered().getChipTextString(context))
+ }
+
+ @Test
+ fun transferToThisDeviceSucceeded_appIcon_deviceName_noLoadingIcon_noFailureIcon() {
+ val state = transferToThisDeviceSucceeded()
+ controllerSender.displayChip(state)
val chipView = getChipView()
assertThat(chipView.getAppIconView().drawable).isEqualTo(appIconDrawable)
assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_ICON_CONTENT_DESC)
- assertThat(chipView.getChipText()).contains(DEVICE_NAME)
+ assertThat(chipView.getChipText()).isEqualTo(state.getChipTextString(context))
assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE)
+ assertThat(chipView.getFailureIcon().visibility).isEqualTo(View.GONE)
}
@Test
- fun transferSucceededNullUndoRunnable_noUndo() {
- controllerSender.displayChip(transferSucceeded(undoRunnable = null))
+ fun transferToThisDeviceSucceeded_nullUndoRunnable_noUndo() {
+ controllerSender.displayChip(transferToThisDeviceSucceeded(undoCallback = null))
val chipView = getChipView()
assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
}
@Test
- fun transferSucceededWithUndoRunnable_undoWithClick() {
- controllerSender.displayChip(transferSucceeded { })
+ fun transferToThisDeviceSucceeded_withUndoRunnable_undoWithClick() {
+ val undoCallback = object : IUndoTransferCallback.Stub() {
+ override fun onUndoTriggered() {}
+ }
+ controllerSender.displayChip(transferToThisDeviceSucceeded(undoCallback))
val chipView = getChipView()
assertThat(chipView.getUndoButton().visibility).isEqualTo(View.VISIBLE)
@@ -181,48 +203,93 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
}
@Test
- fun transferSucceededWithUndoRunnable_undoButtonClickRunsRunnable() {
- var runnableRun = false
- val runnable = Runnable { runnableRun = true }
+ fun transferToThisDeviceSucceeded_withUndoRunnable_undoButtonClickRunsRunnable() {
+ var undoCallbackCalled = false
+ val undoCallback = object : IUndoTransferCallback.Stub() {
+ override fun onUndoTriggered() {
+ undoCallbackCalled = true
+ }
+ }
+
+ controllerSender.displayChip(transferToThisDeviceSucceeded(undoCallback))
+ getChipView().getUndoButton().performClick()
+
+ assertThat(undoCallbackCalled).isTrue()
+ }
+
+ @Test
+ fun transferToThisDeviceSucceeded_undoButtonClick_switchesToTransferToReceiverTriggered() {
+ val undoCallback = object : IUndoTransferCallback.Stub() {
+ override fun onUndoTriggered() {}
+ }
+ controllerSender.displayChip(transferToThisDeviceSucceeded(undoCallback))
- controllerSender.displayChip(transferSucceeded(undoRunnable = runnable))
getChipView().getUndoButton().performClick()
- assertThat(runnableRun).isTrue()
+ assertThat(getChipView().getChipText())
+ .isEqualTo(transferToReceiverTriggered().getChipTextString(context))
+ }
+
+ @Test
+ fun transferFailed_appIcon_noDeviceName_noLoadingIcon_noUndo_failureIcon() {
+ val state = transferFailed()
+ controllerSender.displayChip(state)
+
+ val chipView = getChipView()
+ assertThat(chipView.getAppIconView().drawable).isEqualTo(appIconDrawable)
+ assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_ICON_CONTENT_DESC)
+ assertThat(chipView.getChipText()).isEqualTo(state.getChipTextString(context))
+ assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE)
+ assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
+ assertThat(chipView.getFailureIcon().visibility).isEqualTo(View.VISIBLE)
}
@Test
- fun changeFromCloserToStartToTransferInitiated_loadingIconAppears() {
+ fun changeFromCloserToStartToTransferTriggered_loadingIconAppears() {
controllerSender.displayChip(moveCloserToStartCast())
- controllerSender.displayChip(transferInitiated())
+ controllerSender.displayChip(transferToReceiverTriggered())
assertThat(getChipView().getLoadingIconVisibility()).isEqualTo(View.VISIBLE)
}
@Test
- fun changeFromTransferInitiatedToTransferSucceeded_loadingIconDisappears() {
- controllerSender.displayChip(transferInitiated())
- controllerSender.displayChip(transferSucceeded())
+ fun changeFromTransferTriggeredToTransferSucceeded_loadingIconDisappears() {
+ controllerSender.displayChip(transferToReceiverTriggered())
+ controllerSender.displayChip(transferToReceiverSucceeded())
assertThat(getChipView().getLoadingIconVisibility()).isEqualTo(View.GONE)
}
@Test
- fun changeFromTransferInitiatedToTransferSucceeded_undoButtonAppears() {
- controllerSender.displayChip(transferInitiated())
- controllerSender.displayChip(transferSucceeded { })
+ fun changeFromTransferTriggeredToTransferSucceeded_undoButtonAppears() {
+ controllerSender.displayChip(transferToReceiverTriggered())
+ controllerSender.displayChip(
+ transferToReceiverSucceeded(
+ object : IUndoTransferCallback.Stub() {
+ override fun onUndoTriggered() {}
+ }
+ )
+ )
assertThat(getChipView().getUndoButton().visibility).isEqualTo(View.VISIBLE)
}
@Test
fun changeFromTransferSucceededToMoveCloserToStart_undoButtonDisappears() {
- controllerSender.displayChip(transferSucceeded())
+ controllerSender.displayChip(transferToReceiverSucceeded())
controllerSender.displayChip(moveCloserToStartCast())
assertThat(getChipView().getUndoButton().visibility).isEqualTo(View.GONE)
}
+ @Test
+ fun changeFromTransferTriggeredToTransferFailed_failureIconAppears() {
+ controllerSender.displayChip(transferToReceiverTriggered())
+ controllerSender.displayChip(transferFailed())
+
+ assertThat(getChipView().getFailureIcon().visibility).isEqualTo(View.VISIBLE)
+ }
+
private fun LinearLayout.getAppIconView() = this.requireViewById<ImageView>(R.id.app_icon)
private fun LinearLayout.getChipText(): String =
@@ -233,6 +300,8 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
private fun LinearLayout.getUndoButton(): View = this.requireViewById(R.id.undo)
+ private fun LinearLayout.getFailureIcon(): View = this.requireViewById(R.id.failure_icon)
+
private fun getChipView(): LinearLayout {
val viewCaptor = ArgumentCaptor.forClass(View::class.java)
verify(windowManager).addView(viewCaptor.capture(), any())
@@ -244,18 +313,32 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
MoveCloserToStartCast(appIconDrawable, APP_ICON_CONTENT_DESC, DEVICE_NAME)
/** Helper method providing default parameters to not clutter up the tests. */
- private fun transferInitiated(
- future: Future<Runnable?> = TEST_FUTURE
- ) = TransferInitiated(appIconDrawable, APP_ICON_CONTENT_DESC, DEVICE_NAME, future)
+ private fun moveCloserToEndCast() =
+ MoveCloserToEndCast(appIconDrawable, APP_ICON_CONTENT_DESC, DEVICE_NAME)
+
+ /** Helper method providing default parameters to not clutter up the tests. */
+ private fun transferToReceiverTriggered() =
+ TransferToReceiverTriggered(appIconDrawable, APP_ICON_CONTENT_DESC, DEVICE_NAME)
+
+ /** Helper method providing default parameters to not clutter up the tests. */
+ private fun transferToThisDeviceTriggered() =
+ TransferToThisDeviceTriggered(appIconDrawable, APP_ICON_CONTENT_DESC)
+
+ /** Helper method providing default parameters to not clutter up the tests. */
+ private fun transferToReceiverSucceeded(undoCallback: IUndoTransferCallback? = null) =
+ TransferToReceiverSucceeded(
+ appIconDrawable, APP_ICON_CONTENT_DESC, DEVICE_NAME, undoCallback
+ )
+
+ /** Helper method providing default parameters to not clutter up the tests. */
+ private fun transferToThisDeviceSucceeded(undoCallback: IUndoTransferCallback? = null) =
+ TransferToThisDeviceSucceeded(
+ appIconDrawable, APP_ICON_CONTENT_DESC, DEVICE_NAME, undoCallback
+ )
/** Helper method providing default parameters to not clutter up the tests. */
- private fun transferSucceeded(
- undoRunnable: Runnable? = null
- ) = TransferSucceeded(appIconDrawable, APP_ICON_CONTENT_DESC, DEVICE_NAME, undoRunnable)
+ private fun transferFailed() = TransferFailed(appIconDrawable, APP_ICON_CONTENT_DESC)
}
private const val DEVICE_NAME = "My Tablet"
private const val APP_ICON_CONTENT_DESC = "Content description"
-// Use a settable future that hasn't yet been set so that we don't immediately switch to the success
-// state.
-private val TEST_FUTURE: SettableFuture<Runnable?> = SettableFuture.create()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderServiceTest.kt
index 8f64698a5a6c..11b727ec507c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderServiceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderServiceTest.kt
@@ -4,7 +4,9 @@ import android.media.MediaRoute2Info
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.shared.mediattt.DeviceInfo
-import com.android.systemui.shared.mediattt.IDeviceSenderCallback
+import com.android.systemui.shared.mediattt.IDeviceSenderService
+import com.android.systemui.shared.mediattt.IUndoTransferCallback
+import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.capture
import com.google.common.truth.Truth.assertThat
@@ -17,8 +19,7 @@ import org.mockito.MockitoAnnotations
@SmallTest
class MediaTttSenderServiceTest : SysuiTestCase() {
- private lateinit var service: MediaTttSenderService
- private lateinit var callback: IDeviceSenderCallback
+ private lateinit var service: IDeviceSenderService
@Mock
private lateinit var controller: MediaTttChipControllerSender
@@ -30,19 +31,94 @@ class MediaTttSenderServiceTest : SysuiTestCase() {
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- service = MediaTttSenderService(context, controller)
- callback = IDeviceSenderCallback.Stub.asInterface(service.onBind(null))
+ val mediaTttSenderService = MediaTttSenderService(context, controller)
+ service = IDeviceSenderService.Stub.asInterface(mediaTttSenderService.onBind(null))
}
@Test
- fun closeToReceiverToStartCast_controllerTriggeredWithMoveCloserToStartCastState() {
+ fun closeToReceiverToStartCast_controllerTriggeredWithCorrectState() {
val name = "Fake name"
- callback.closeToReceiverToStartCast(mediaInfo, DeviceInfo(name))
+ service.closeToReceiverToStartCast(mediaInfo, DeviceInfo(name))
val chipStateCaptor = argumentCaptor<MoveCloserToStartCast>()
verify(controller).displayChip(capture(chipStateCaptor))
val chipState = chipStateCaptor.value!!
- assertThat(chipState.otherDeviceName).isEqualTo(name)
+ assertThat(chipState.getChipTextString(context)).contains(name)
+ }
+
+ @Test
+ fun closeToReceiverToEndCast_controllerTriggeredWithCorrectState() {
+ val name = "Fake name"
+ service.closeToReceiverToEndCast(mediaInfo, DeviceInfo(name))
+
+ val chipStateCaptor = argumentCaptor<MoveCloserToEndCast>()
+ verify(controller).displayChip(capture(chipStateCaptor))
+
+ val chipState = chipStateCaptor.value!!
+ assertThat(chipState.getChipTextString(context)).contains(name)
+ }
+
+ @Test
+ fun transferToThisDeviceTriggered_controllerTriggeredWithCorrectState() {
+ service.transferToThisDeviceTriggered(mediaInfo, DeviceInfo("Fake name"))
+
+ verify(controller).displayChip(any<TransferToThisDeviceTriggered>())
+ }
+
+ @Test
+ fun transferToReceiverTriggered_controllerTriggeredWithCorrectState() {
+ val name = "Fake name"
+ service.transferToReceiverTriggered(mediaInfo, DeviceInfo(name))
+
+ val chipStateCaptor = argumentCaptor<TransferToReceiverTriggered>()
+ verify(controller).displayChip(capture(chipStateCaptor))
+
+ val chipState = chipStateCaptor.value!!
+ assertThat(chipState.getChipTextString(context)).contains(name)
+ }
+
+ @Test
+ fun transferToReceiverSucceeded_controllerTriggeredWithCorrectState() {
+ val name = "Fake name"
+ val undoCallback = object : IUndoTransferCallback.Stub() {
+ override fun onUndoTriggered() {}
+ }
+ service.transferToReceiverSucceeded(mediaInfo, DeviceInfo(name), undoCallback)
+
+ val chipStateCaptor = argumentCaptor<TransferToReceiverSucceeded>()
+ verify(controller).displayChip(capture(chipStateCaptor))
+
+ val chipState = chipStateCaptor.value!!
+ assertThat(chipState.getChipTextString(context)).contains(name)
+ assertThat(chipState.undoCallback).isEqualTo(undoCallback)
+ }
+
+ @Test
+ fun transferToThisDeviceSucceeded_controllerTriggeredWithCorrectState() {
+ val undoCallback = object : IUndoTransferCallback.Stub() {
+ override fun onUndoTriggered() {}
+ }
+ service.transferToThisDeviceSucceeded(mediaInfo, DeviceInfo("name"), undoCallback)
+
+ val chipStateCaptor = argumentCaptor<TransferToThisDeviceSucceeded>()
+ verify(controller).displayChip(capture(chipStateCaptor))
+
+ val chipState = chipStateCaptor.value!!
+ assertThat(chipState.undoCallback).isEqualTo(undoCallback)
+ }
+
+ @Test
+ fun transferFailed_controllerTriggeredWithTransferFailedState() {
+ service.transferFailed(mediaInfo, DeviceInfo("Fake name"))
+
+ verify(controller).displayChip(any<TransferFailed>())
+ }
+
+ @Test
+ fun noLongerCloseToReceiver_controllerRemoveChipTriggered() {
+ service.noLongerCloseToReceiver(mediaInfo, DeviceInfo("Fake name"))
+
+ verify(controller).removeChip()
}
}
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 3e8e8748a679..73d2b0bf1a0f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
@@ -47,6 +47,7 @@ import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.phone.AutoHideController;
import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.wm.shell.back.BackAnimation;
import com.android.wm.shell.pip.Pip;
import org.junit.After;
@@ -92,7 +93,8 @@ public class NavigationBarControllerTest extends SysuiTestCase {
mock(DumpManager.class),
mock(AutoHideController.class),
mock(LightBarController.class),
- Optional.of(mock(Pip.class))));
+ Optional.of(mock(Pip.class)),
+ Optional.of(mock(BackAnimation.class))));
initializeNavigationBars();
}
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 5003013358be..9ca898b9dea9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
@@ -95,6 +95,7 @@ import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.utils.leaks.LeakCheckedTest;
+import com.android.wm.shell.back.BackAnimation;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
import com.android.wm.shell.pip.Pip;
@@ -105,7 +106,6 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.mockito.Spy;
import java.util.Optional;
@@ -383,7 +383,8 @@ public class NavigationBarTest extends SysuiTestCase {
mAutoHideController,
mAutoHideControllerFactory,
Optional.of(mTelecomManager),
- mInputMethodManager);
+ mInputMethodManager,
+ Optional.of(mock(BackAnimation.class)));
return spy(factory.create(context));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerControllerTest.java
index 03a0da7d91fb..4a6bbbcf1d6b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerControllerTest.java
@@ -24,6 +24,7 @@ import static com.android.systemui.qrcodescanner.controller.QRCodeScannerControl
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -73,7 +74,7 @@ public class QRCodeScannerControllerTest extends SysuiTestCase {
private DeviceConfigProxyFake mProxyFake;
private void setUpLocal(String deviceConfigActivity, String defaultActivity,
- boolean validateActivity, boolean enableSetting) {
+ boolean validateActivity, boolean enableSetting, boolean enableOnLockScreen) {
MockitoAnnotations.initMocks(this);
int enableSettingInt = enableSetting ? 1 : 0;
@@ -91,6 +92,8 @@ public class QRCodeScannerControllerTest extends SysuiTestCase {
when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA)).thenReturn(true);
mContext.getOrCreateTestableResources().addOverride(R.string.def_qr_code_component,
defaultActivity);
+ mContext.getOrCreateTestableResources().addOverride(
+ android.R.bool.config_enableQrCodeScannerOnLockScreen, enableOnLockScreen);
mProxyFake = new DeviceConfigProxyFake();
mProxyFake.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
@@ -126,7 +129,8 @@ public class QRCodeScannerControllerTest extends SysuiTestCase {
@Test
public void qrCodeScannerInit_withoutDefaultValue() {
setUpLocal(/* deviceConfigActivity */ null, /* defaultActivity */
- "", /* validateActivity */ true, /* enableSetting */true);
+ "", /* validateActivity */ true, /* enableSetting */ true,
+ /* enableOnLockScreen */ true);
verifyActivityDetails(null);
assertThat(mController.isEnabledForLockScreenButton()).isFalse();
assertThat(mController.isEnabledForQuickSettings()).isFalse();
@@ -135,7 +139,8 @@ public class QRCodeScannerControllerTest extends SysuiTestCase {
@Test
public void qrCodeScannerInit_withIncorrectDefaultValue() {
setUpLocal(/* deviceConfigActivity */ null, /* defaultActivity */
- "abc/.def", /* validateActivity */ false, /* enableSetting */ true);
+ "abc/.def", /* validateActivity */ false, /* enableSetting */ true,
+ /* enableOnLockScreen */ true);
verifyActivityDetails(null);
assertThat(mController.isEnabledForLockScreenButton()).isFalse();
}
@@ -143,7 +148,8 @@ public class QRCodeScannerControllerTest extends SysuiTestCase {
@Test
public void qrCodeScannerInit_withCorrectDefaultValue() {
setUpLocal(/* deviceConfigActivity */ null, /* defaultActivity */
- "abc/.def", /* validateActivity */ true, /* enableSetting */true);
+ "abc/.def", /* validateActivity */ true, /* enableSetting */true,
+ /* enableOnLockScreen */ true);
verifyActivityDetails("abc/.def");
assertThat(mController.isEnabledForLockScreenButton()).isTrue();
assertThat(mController.isEnabledForQuickSettings()).isTrue();
@@ -152,7 +158,8 @@ public class QRCodeScannerControllerTest extends SysuiTestCase {
@Test
public void qrCodeScannerInit_withCorrectDeviceConfig() {
setUpLocal(/* deviceConfigActivity */ "abc/.def", /* defaultActivity */
- "", /* validateActivity */ true, /* enableSetting */true);
+ "", /* validateActivity */ true, /* enableSetting */true,
+ /* enableOnLockScreen */ true);
verifyActivityDetails("abc/.def");
assertThat(mController.isEnabledForLockScreenButton()).isTrue();
assertThat(mController.isEnabledForQuickSettings()).isTrue();
@@ -161,7 +168,8 @@ public class QRCodeScannerControllerTest extends SysuiTestCase {
@Test
public void qrCodeScannerInit_withCorrectDeviceConfig_withCorrectDefaultValue() {
setUpLocal(/* deviceConfigActivity */ "abc/.def", /* defaultActivity */
- "xyz/.qrs", /* validateActivity */true, /* enableSetting */ true);
+ "xyz/.qrs", /* validateActivity */true, /* enableSetting */ true,
+ /* enableOnLockScreen */ true);
verifyActivityDetails("abc/.def");
assertThat(mController.isEnabledForLockScreenButton()).isTrue();
assertThat(mController.isEnabledForQuickSettings()).isTrue();
@@ -170,7 +178,8 @@ public class QRCodeScannerControllerTest extends SysuiTestCase {
@Test
public void qrCodeScannerInit_withCorrectDeviceConfig_fullActivity() {
setUpLocal(/* deviceConfigActivity */ "abc/abc.def", /* defaultActivity */
- "", /* validateActivity */ true, /* enableSetting */ true);
+ "", /* validateActivity */ true, /* enableSetting */ true,
+ /* enableOnLockScreen */ true);
verifyActivityDetails("abc/abc.def");
assertThat(mController.isEnabledForLockScreenButton()).isTrue();
assertThat(mController.isEnabledForQuickSettings()).isTrue();
@@ -179,7 +188,8 @@ public class QRCodeScannerControllerTest extends SysuiTestCase {
@Test
public void qrCodeScannerInit_withIncorrectDeviceConfig() {
setUpLocal(/* deviceConfigActivity */ "def/.efg", /* defaultActivity */
- "", /* validateActivity */ false, /* enableSetting */ true);
+ "", /* validateActivity */ false, /* enableSetting */ true,
+ /* enableOnLockScreen */ true);
verifyActivityDetails(null);
assertThat(mController.isEnabledForLockScreenButton()).isFalse();
assertThat(mController.isEnabledForQuickSettings()).isFalse();
@@ -188,7 +198,8 @@ public class QRCodeScannerControllerTest extends SysuiTestCase {
@Test
public void verifyDeviceConfigChange_withDefaultActivity() {
setUpLocal(/* deviceConfigActivity */ null, /* defaultActivity */
- "abc/.def", /* validateActivity */ true, /* enableSetting */true);
+ "abc/.def", /* validateActivity */ true, /* enableSetting */true,
+ /* enableOnLockScreen */ true);
verifyActivityDetails("abc/.def");
assertThat(mController.isEnabledForLockScreenButton()).isTrue();
assertThat(mController.isEnabledForQuickSettings()).isTrue();
@@ -214,7 +225,8 @@ public class QRCodeScannerControllerTest extends SysuiTestCase {
@Test
public void verifyDeviceConfigChange_withoutDefaultActivity() {
setUpLocal(/* deviceConfigActivity */ null, /* defaultActivity */
- "", /* validateActivity */ true, /* enableSetting */ true);
+ "", /* validateActivity */ true, /* enableSetting */ true,
+ /* enableOnLockScreen */ true);
verifyActivityDetails(null);
assertThat(mController.isEnabledForLockScreenButton()).isFalse();
assertThat(mController.isEnabledForQuickSettings()).isFalse();
@@ -239,7 +251,8 @@ public class QRCodeScannerControllerTest extends SysuiTestCase {
@Test
public void verifyDeviceConfigChangeToSameValue() {
setUpLocal(/* deviceConfigActivity */ null, /* defaultActivity */
- "", /* validateActivity */ true, /* enableSetting */true);
+ "", /* validateActivity */ true, /* enableSetting */true,
+ /* enableOnLockScreen */ true);
mProxyFake.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
SystemUiDeviceConfigFlags.DEFAULT_QR_CODE_SCANNER,
@@ -261,7 +274,8 @@ public class QRCodeScannerControllerTest extends SysuiTestCase {
@Test
public void verifyPreferenceChange() {
setUpLocal(/* deviceConfigActivity */ null, /* defaultActivity */
- "abc/.def", /* validateActivity */ true, /* enableSetting */true);
+ "abc/.def", /* validateActivity */ true, /* enableSetting */true,
+ /* enableOnLockScreen */ true);
mSecureSettings.putStringForUser(LOCK_SCREEN_SHOW_QR_CODE_SCANNER, "0",
UserHandle.USER_CURRENT);
mSecureSettings.putStringForUser(LOCK_SCREEN_SHOW_QR_CODE_SCANNER, "0",
@@ -278,7 +292,8 @@ public class QRCodeScannerControllerTest extends SysuiTestCase {
@Test
public void verifyPreferenceChangeToSameValue() {
setUpLocal(/* deviceConfigActivity */ null, /* defaultActivity */
- "abc/.def", /* validateActivity */ true, /* enableSetting */true);
+ "abc/.def", /* validateActivity */ true, /* enableSetting */true,
+ /* enableOnLockScreen */ true);
verifyActivityDetails("abc/.def");
assertThat(mController.isEnabledForLockScreenButton()).isTrue();
assertThat(mController.isEnabledForQuickSettings()).isTrue();
@@ -301,7 +316,8 @@ public class QRCodeScannerControllerTest extends SysuiTestCase {
@Test
public void verifyUnregisterRegisterChangeObservers() {
setUpLocal(/* deviceConfigActivity */ null, /* defaultActivity */
- "abc/.def", /* validateActivity */ true, /* enableSetting */true);
+ "abc/.def", /* validateActivity */ true, /* enableSetting */true,
+ /* enableOnLockScreen */ true);
verifyActivityDetails("abc/.def");
assertThat(mController.isEnabledForLockScreenButton()).isTrue();
assertThat(mController.isEnabledForQuickSettings()).isTrue();
@@ -312,7 +328,7 @@ public class QRCodeScannerControllerTest extends SysuiTestCase {
assertThat(mController.isEnabledForLockScreenButton()).isFalse();
assertThat(mController.isEnabledForQuickSettings()).isFalse();
- // Unregister once again and make sure, it affect affect the next register event
+ // Unregister once again and make sure it affects the next register event
mController.unregisterQRCodeScannerChangeObservers(DEFAULT_QR_CODE_SCANNER_CHANGE,
QR_CODE_SCANNER_PREFERENCE_CHANGE);
mController.registerQRCodeScannerChangeObservers(DEFAULT_QR_CODE_SCANNER_CHANGE,
@@ -321,4 +337,15 @@ public class QRCodeScannerControllerTest extends SysuiTestCase {
assertThat(mController.isEnabledForLockScreenButton()).isTrue();
assertThat(mController.isEnabledForQuickSettings()).isTrue();
}
+
+ @Test
+ public void verifyDisableLockscreenButton() {
+ setUpLocal(/* deviceConfigActivity */ null, /* defaultActivity */
+ "abc/.def", /* validateActivity */ true, /* enableSetting */true,
+ /* enableOnLockScreen */ false);
+ assertThat(mController.getIntent()).isNotNull();
+ assertThat(mController.isEnabledForLockScreenButton()).isFalse();
+ assertThat(mController.isEnabledForQuickSettings()).isTrue();
+ assertThat(getSettingsQRCodeDefaultComponent()).isNull();
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt
index 26f04fc4e7b5..354bb5192251 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt
@@ -54,8 +54,6 @@ class FooterActionsControllerTest : LeakCheckedTest() {
@Mock
private lateinit var userInfoController: UserInfoController
@Mock
- private lateinit var qsPanelController: QSPanelController
- @Mock
private lateinit var multiUserSwitchController: MultiUserSwitchController
@Mock
private lateinit var globalActionsDialog: GlobalActionsDialogLite
@@ -81,7 +79,7 @@ class FooterActionsControllerTest : LeakCheckedTest() {
view = LayoutInflater.from(context)
.inflate(R.layout.footer_actions, null) as FooterActionsView
- controller = FooterActionsController(view, qsPanelController, activityStarter,
+ controller = FooterActionsController(view, activityStarter,
userManager, userTracker, userInfoController, multiUserSwitchController,
deviceProvisionedController, falsingManager, metricsLogger, fakeTunerService,
globalActionsDialog, uiEventLogger, showPMLiteButton = true,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java
index 8b19c50f915e..f43e68f3e575 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java
@@ -18,7 +18,11 @@ package com.android.systemui.qs;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import android.content.ClipData;
@@ -31,6 +35,8 @@ import android.widget.TextView;
import androidx.test.filters.SmallTest;
import com.android.systemui.R;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.utils.leaks.LeakCheckedTest;
@@ -60,13 +66,20 @@ public class QSFooterViewControllerTest extends LeakCheckedTest {
private TextView mBuildText;
@Mock
private FooterActionsController mFooterActionsController;
+ @Mock
+ private FalsingManager mFalsingManager;
+ @Mock
+ private ActivityStarter mActivityStarter;
private QSFooterViewController mController;
+ private View mEditButton;
@Before
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
+ mEditButton = new View(mContext);
+
injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
mContext.addMockSystemService(ClipboardManager.class, mClipboardManager);
@@ -77,9 +90,11 @@ public class QSFooterViewControllerTest extends LeakCheckedTest {
when(mView.isAttachedToWindow()).thenReturn(true);
when(mView.findViewById(R.id.build)).thenReturn(mBuildText);
+ when(mView.findViewById(android.R.id.edit)).thenReturn(mEditButton);
- mController = new QSFooterViewController(mView, mUserTracker, mQSPanelController,
- mQuickQSPanelController, mFooterActionsController);
+ mController = new QSFooterViewController(mView, mUserTracker, mFalsingManager,
+ mActivityStarter, mQSPanelController, mQuickQSPanelController,
+ mFooterActionsController);
mController.init();
}
@@ -99,4 +114,27 @@ public class QSFooterViewControllerTest extends LeakCheckedTest {
verify(mClipboardManager).setPrimaryClip(captor.capture());
assertThat(captor.getValue().getItemAt(0).getText()).isEqualTo(text);
}
+
+ @Test
+ public void testEditButton_falseTap() {
+ when(mFalsingManager.isFalseTap(anyInt())).thenReturn(true);
+
+ mEditButton.performClick();
+
+ verify(mQSPanelController, never()).showEdit(any());
+ verifyZeroInteractions(mActivityStarter);
+ }
+
+ @Test
+ public void testEditButton_realTap() {
+ when(mFalsingManager.isFalseTap(anyInt())).thenReturn(false);
+
+ mEditButton.performClick();
+
+ ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class);
+
+ verify(mActivityStarter).postQSRunnableDismissingKeyguard(captor.capture());
+ captor.getValue().run();
+ verify(mQSPanelController).showEdit(mEditButton);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
index 0bce621c3b02..91e5d33d9c9e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
@@ -302,6 +302,40 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() {
}
@Test
+ fun ignoreBlurForUnlock_ignores() {
+ notificationShadeDepthController.onPanelExpansionChanged(
+ rawFraction = 1f, expanded = true, tracking = false
+ )
+ `when`(shadeAnimation.radius).thenReturn(maxBlur.toFloat())
+
+ notificationShadeDepthController.blursDisabledForAppLaunch = false
+ notificationShadeDepthController.blursDisabledForUnlock = true
+
+ notificationShadeDepthController.updateBlurCallback.doFrame(0)
+
+ // Since we are ignoring blurs for unlock, we should be applying blur = 0 despite setting it
+ // to maxBlur above.
+ verify(blurUtils).applyBlur(any(), eq(0), eq(false))
+ }
+
+ @Test
+ fun ignoreBlurForUnlock_doesNotIgnore() {
+ notificationShadeDepthController.onPanelExpansionChanged(
+ rawFraction = 1f, expanded = true, tracking = false
+ )
+ `when`(shadeAnimation.radius).thenReturn(maxBlur.toFloat())
+
+ notificationShadeDepthController.blursDisabledForAppLaunch = false
+ notificationShadeDepthController.blursDisabledForUnlock = false
+
+ notificationShadeDepthController.updateBlurCallback.doFrame(0)
+
+ // Since we are not ignoring blurs for unlock (or app launch), we should apply the blur we
+ // returned above (maxBlur).
+ verify(blurUtils).applyBlur(any(), eq(maxBlur), eq(false))
+ }
+
+ @Test
fun brightnessMirrorVisible_whenVisible() {
notificationShadeDepthController.brightnessMirrorVisible = true
verify(brightnessSpring).animateTo(eq(maxBlur), any())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
index 1961ab269267..188baaf682b0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
@@ -561,6 +561,10 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
override fun setMediaTarget(target: SmartspaceTarget?) {
}
+
+ override fun getSelectedPage(): Int {
+ return -1
+ }
})
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index dc83c0d08291..f2b7bf515c45 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.notification;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED;
import static android.service.notification.NotificationListenerService.REASON_CANCEL;
import static com.android.systemui.statusbar.notification.NotificationEntryManager.UNDEFINED_DISMISS_REASON;
@@ -428,6 +429,18 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
}
@Test
+ public void testNotifyChannelModified_notifiesListeners() {
+ NotificationChannel channel = mock(NotificationChannel.class);
+ String pkg = "PKG";
+ mEntryManager.notifyChannelModified(pkg, UserHandle.CURRENT, channel,
+ NOTIFICATION_CHANNEL_OR_GROUP_UPDATED);
+ verify(mNotifCollectionListener).onNotificationChannelModified(eq(pkg),
+ eq(UserHandle.CURRENT), eq(channel), eq(NOTIFICATION_CHANNEL_OR_GROUP_UPDATED));
+ verify(mEntryListener).onNotificationChannelModified(eq(pkg),
+ eq(UserHandle.CURRENT), eq(channel), eq(NOTIFICATION_CHANNEL_OR_GROUP_UPDATED));
+ }
+
+ @Test
public void testLifetimeExtenders_ifNotificationIsRetainedItIsntRemoved() {
// GIVEN an entry manager with a notification
mEntryManager.addActiveNotificationForTest(mEntry);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
index f08180dd3b9b..706800940fd1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
@@ -19,6 +19,7 @@ package com.android.systemui.statusbar.notification.collection;
import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
import static android.app.Notification.FLAG_NO_CLEAR;
import static android.app.Notification.FLAG_ONGOING_EVENT;
+import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED;
import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
import static android.service.notification.NotificationListenerService.REASON_CANCEL;
import static android.service.notification.NotificationListenerService.REASON_CLICK;
@@ -53,6 +54,8 @@ import static java.util.Objects.requireNonNull;
import android.annotation.Nullable;
import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
import android.os.Handler;
import android.os.RemoteException;
import android.service.notification.NotificationListenerService.Ranking;
@@ -336,6 +339,37 @@ public class NotifCollectionTest extends SysuiTestCase {
}
@Test
+ public void testEventDispatchedWhenChannelChanged() {
+ // GIVEN a collection with one notif that has a channel
+ NotificationEntryBuilder neb = buildNotif(TEST_PACKAGE, 48);
+ NotificationChannel channel = new NotificationChannel(
+ "channelId",
+ "channelName",
+ NotificationManager.IMPORTANCE_DEFAULT);
+ neb.setChannel(channel);
+
+ NotifEvent notif = mNoMan.postNotif(neb);
+ NotificationEntry entry = mCollectionListener.getEntry(notif.key);
+ clearInvocations(mCollectionListener);
+
+
+ // WHEN a notif channel is modified
+ channel.setAllowBubbles(true);
+ mNoMan.issueChannelModification(
+ TEST_PACKAGE,
+ entry.getSbn().getUser(),
+ channel,
+ NOTIFICATION_CHANNEL_OR_GROUP_UPDATED);
+
+ // THEN the listener is notified
+ mListenerInOrder.verify(mCollectionListener).onNotificationChannelModified(
+ TEST_PACKAGE,
+ entry.getSbn().getUser(),
+ channel,
+ NOTIFICATION_CHANNEL_OR_GROUP_UPDATED);
+ }
+
+ @Test
public void testRankingsAreUpdatedForOtherNotifs() {
// GIVEN a collection with one notif
NotifEvent notif1 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 3)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
index b832577c16ac..25dd23a955e6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
@@ -1968,7 +1968,7 @@ public class ShadeListBuilderTest extends SysuiTestCase {
}
@Override
- public int compare(ListEntry o1, ListEntry o2) {
+ public int compare(@NonNull ListEntry o1, @NonNull ListEntry o2) {
boolean contains1 = mPreferredPackages.contains(
o1.getRepresentativeEntry().getSbn().getPackageName());
boolean contains2 = mPreferredPackages.contains(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java
index f2e7081e096b..bc32759a9938 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java
@@ -28,6 +28,10 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.app.Person;
+import android.content.Intent;
import android.graphics.Color;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
@@ -151,4 +155,35 @@ public class AppOpsCoordinatorTest extends SysuiTestCase {
// THEN the entry is NOT in the fgs section
assertFalse(mFgsSection.isInSection(mEntryBuilder.build()));
}
+
+ @Test
+ public void testIncludeCallInSection_importanceDefault() {
+ // GIVEN the notification represents a call with > min importance
+ mEntryBuilder
+ .setImportance(IMPORTANCE_DEFAULT)
+ .modifyNotification(mContext)
+ .setStyle(makeCallStyle());
+
+ // THEN the entry is in the fgs section
+ assertTrue(mFgsSection.isInSection(mEntryBuilder.build()));
+ }
+
+ @Test
+ public void testDiscludeCallInSection_importanceMin() {
+ // GIVEN the notification represents a call with min importance
+ mEntryBuilder
+ .setImportance(IMPORTANCE_MIN)
+ .modifyNotification(mContext)
+ .setStyle(makeCallStyle());
+
+ // THEN the entry is NOT in the fgs section
+ assertFalse(mFgsSection.isInSection(mEntryBuilder.build()));
+ }
+
+ private Notification.CallStyle makeCallStyle() {
+ final PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0,
+ new Intent("action"), PendingIntent.FLAG_IMMUTABLE);
+ final Person person = new Person.Builder().setName("person").build();
+ return Notification.CallStyle.forIncomingCall(person, pendingIntent, pendingIntent);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt
index a46b44002812..8deac94214bd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt
@@ -24,18 +24,24 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
+import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifComparator
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner
import com.android.systemui.statusbar.notification.collection.render.NodeController
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_IMPORTANT_PERSON
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_PERSON
+import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.withArgCaptor
+import com.google.common.truth.Truth.assertThat
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
+import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
import org.mockito.Mockito.`when` as whenever
@@ -47,12 +53,15 @@ class ConversationCoordinatorTest : SysuiTestCase() {
// captured listeners and pluggables:
private lateinit var promoter: NotifPromoter
private lateinit var peopleSectioner: NotifSectioner
+ private lateinit var peopleComparator: NotifComparator
@Mock private lateinit var pipeline: NotifPipeline
@Mock private lateinit var peopleNotificationIdentifier: PeopleNotificationIdentifier
@Mock private lateinit var channel: NotificationChannel
@Mock private lateinit var headerController: NodeController
private lateinit var entry: NotificationEntry
+ private lateinit var entryA: NotificationEntry
+ private lateinit var entryB: NotificationEntry
private lateinit var coordinator: ConversationCoordinator
@@ -70,8 +79,15 @@ class ConversationCoordinatorTest : SysuiTestCase() {
}
peopleSectioner = coordinator.sectioner
+ peopleComparator = coordinator.comparator
entry = NotificationEntryBuilder().setChannel(channel).build()
+
+ val section = NotifSection(peopleSectioner, 0)
+ entryA = NotificationEntryBuilder().setChannel(channel)
+ .setSection(section).setTag("A").build()
+ entryB = NotificationEntryBuilder().setChannel(channel)
+ .setSection(section).setTag("B").build()
}
@Test
@@ -90,4 +106,36 @@ class ConversationCoordinatorTest : SysuiTestCase() {
assertTrue(peopleSectioner.isInSection(entry))
assertFalse(peopleSectioner.isInSection(NotificationEntryBuilder().build()))
}
+
+ @Test
+ fun testComparatorIgnoresFromOtherSection() {
+ val e1 = NotificationEntryBuilder().setId(1).setChannel(channel).build()
+ val e2 = NotificationEntryBuilder().setId(2).setChannel(channel).build()
+
+ // wrong section -- never classify
+ assertThat(peopleComparator.compare(e1, e2)).isEqualTo(0)
+ verify(peopleNotificationIdentifier, never()).getPeopleNotificationType(any())
+ }
+
+ @Test
+ fun testComparatorPutsImportantPeopleFirst() {
+ whenever(peopleNotificationIdentifier.getPeopleNotificationType(entryA))
+ .thenReturn(TYPE_IMPORTANT_PERSON)
+ whenever(peopleNotificationIdentifier.getPeopleNotificationType(entryB))
+ .thenReturn(TYPE_PERSON)
+
+ // only put people notifications in this section
+ assertThat(peopleComparator.compare(entryA, entryB)).isEqualTo(-1)
+ }
+
+ @Test
+ fun testComparatorEquatesPeopleWithSameType() {
+ whenever(peopleNotificationIdentifier.getPeopleNotificationType(entryA))
+ .thenReturn(TYPE_PERSON)
+ whenever(peopleNotificationIdentifier.getPeopleNotificationType(entryB))
+ .thenReturn(TYPE_PERSON)
+
+ // only put people notifications in this section
+ assertThat(peopleComparator.compare(entryA, entryB)).isEqualTo(0)
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.java
deleted file mode 100644
index 8ee892c4be58..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.java
+++ /dev/null
@@ -1,274 +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.systemui.statusbar.notification.collection.coordinator;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.BDDMockito.given;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.NotificationRemoteInputManager;
-import com.android.systemui.statusbar.notification.collection.NotifPipeline;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
-import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter;
-import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner;
-import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
-import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
-import com.android.systemui.statusbar.notification.collection.render.NodeController;
-import com.android.systemui.statusbar.notification.interruption.HeadsUpViewBinder;
-import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
-import com.android.systemui.statusbar.notification.row.NotifBindPipeline.BindCallback;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
-import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
-import com.android.systemui.util.concurrency.FakeExecutor;
-import com.android.systemui.util.time.FakeSystemClock;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.ArrayList;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
-public class HeadsUpCoordinatorTest extends SysuiTestCase {
-
- private HeadsUpCoordinator mCoordinator;
-
- // captured listeners and pluggables:
- private NotifCollectionListener mCollectionListener;
- private NotifPromoter mNotifPromoter;
- private NotifLifetimeExtender mNotifLifetimeExtender;
- private OnHeadsUpChangedListener mOnHeadsUpChangedListener;
- private NotifSectioner mNotifSectioner;
-
- @Mock private NotifPipeline mNotifPipeline;
- @Mock private HeadsUpManager mHeadsUpManager;
- @Mock private HeadsUpViewBinder mHeadsUpViewBinder;
- @Mock private NotificationInterruptStateProvider mNotificationInterruptStateProvider;
- @Mock private NotificationRemoteInputManager mRemoteInputManager;
- @Mock private NotifLifetimeExtender.OnEndLifetimeExtensionCallback mEndLifetimeExtension;
- @Mock private NodeController mHeaderController;
-
- private NotificationEntry mEntry;
- private final FakeSystemClock mClock = new FakeSystemClock();
- private final FakeExecutor mExecutor = new FakeExecutor(mClock);
- private final ArrayList<NotificationEntry> mHuns = new ArrayList();
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
-
- mCoordinator = new HeadsUpCoordinator(
- mHeadsUpManager,
- mHeadsUpViewBinder,
- mNotificationInterruptStateProvider,
- mRemoteInputManager,
- mHeaderController,
- mExecutor);
-
- mCoordinator.attach(mNotifPipeline);
-
- // capture arguments:
- ArgumentCaptor<NotifCollectionListener> notifCollectionCaptor =
- ArgumentCaptor.forClass(NotifCollectionListener.class);
- ArgumentCaptor<NotifPromoter> notifPromoterCaptor =
- ArgumentCaptor.forClass(NotifPromoter.class);
- ArgumentCaptor<NotifLifetimeExtender> notifLifetimeExtenderCaptor =
- ArgumentCaptor.forClass(NotifLifetimeExtender.class);
- ArgumentCaptor<OnHeadsUpChangedListener> headsUpChangedListenerCaptor =
- ArgumentCaptor.forClass(OnHeadsUpChangedListener.class);
-
- verify(mNotifPipeline).addCollectionListener(notifCollectionCaptor.capture());
- verify(mNotifPipeline).addPromoter(notifPromoterCaptor.capture());
- verify(mNotifPipeline).addNotificationLifetimeExtender(
- notifLifetimeExtenderCaptor.capture());
- verify(mHeadsUpManager).addListener(headsUpChangedListenerCaptor.capture());
-
- given(mHeadsUpManager.getAllEntries()).willAnswer(i -> mHuns.stream());
- given(mHeadsUpManager.isAlerting(anyString())).willAnswer(i -> {
- String key = i.getArgument(0);
- for (NotificationEntry entry : mHuns) {
- if (entry.getKey().equals(key)) return true;
- }
- return false;
- });
- when(mHeadsUpManager.getEarliestRemovalTime(anyString())).thenReturn(1000L);
-
- mCollectionListener = notifCollectionCaptor.getValue();
- mNotifPromoter = notifPromoterCaptor.getValue();
- mNotifLifetimeExtender = notifLifetimeExtenderCaptor.getValue();
- mOnHeadsUpChangedListener = headsUpChangedListenerCaptor.getValue();
-
- mNotifSectioner = mCoordinator.getSectioner();
- mNotifLifetimeExtender.setCallback(mEndLifetimeExtension);
- mEntry = new NotificationEntryBuilder().build();
- }
-
- @Test
- public void testCancelStickyNotification() {
- when(mHeadsUpManager.isSticky(anyString())).thenReturn(true);
- addHUN(mEntry);
- when(mHeadsUpManager.canRemoveImmediately(anyString())).thenReturn(false, true);
- when(mHeadsUpManager.getEarliestRemovalTime(anyString())).thenReturn(1000L, 0L);
- assertTrue(mNotifLifetimeExtender.maybeExtendLifetime(mEntry, 0));
- mClock.advanceTime(1000L);
- mExecutor.runAllReady();
- verify(mHeadsUpManager, times(0))
- .removeNotification(anyString(), eq(false));
- verify(mHeadsUpManager, times(1))
- .removeNotification(anyString(), eq(true));
- }
-
- @Test
- public void testCancelUpdatedStickyNotification() {
- when(mHeadsUpManager.isSticky(anyString())).thenReturn(true);
- addHUN(mEntry);
- when(mHeadsUpManager.getEarliestRemovalTime(anyString())).thenReturn(1000L, 500L);
- assertTrue(mNotifLifetimeExtender.maybeExtendLifetime(mEntry, 0));
- mClock.advanceTime(1000L);
- mExecutor.runAllReady();
- verify(mHeadsUpManager, times(0))
- .removeNotification(anyString(), eq(false));
- verify(mHeadsUpManager, times(0))
- .removeNotification(anyString(), eq(true));
- }
-
- @Test
- public void testCancelNotification() {
- when(mHeadsUpManager.isSticky(anyString())).thenReturn(false);
- addHUN(mEntry);
- when(mHeadsUpManager.getEarliestRemovalTime(anyString())).thenReturn(1000L, 500L);
- assertTrue(mNotifLifetimeExtender.maybeExtendLifetime(mEntry, 0));
- mClock.advanceTime(1000L);
- mExecutor.runAllReady();
- verify(mHeadsUpManager, times(1))
- .removeNotification(anyString(), eq(false));
- verify(mHeadsUpManager, times(0))
- .removeNotification(anyString(), eq(true));
- }
-
- @Test
- public void testPromotesCurrentHUN() {
- // GIVEN the current HUN is set to mEntry
- addHUN(mEntry);
-
- // THEN only promote the current HUN, mEntry
- assertTrue(mNotifPromoter.shouldPromoteToTopLevel(mEntry));
- assertFalse(mNotifPromoter.shouldPromoteToTopLevel(new NotificationEntryBuilder()
- .setPkg("test-package2")
- .build()));
- }
-
- @Test
- public void testIncludeInSectionCurrentHUN() {
- // GIVEN the current HUN is set to mEntry
- addHUN(mEntry);
-
- // THEN only section the current HUN, mEntry
- assertTrue(mNotifSectioner.isInSection(mEntry));
- assertFalse(mNotifSectioner.isInSection(new NotificationEntryBuilder()
- .setPkg("test-package")
- .build()));
- }
-
- @Test
- public void testLifetimeExtendsCurrentHUN() {
- // GIVEN there is a HUN, mEntry
- addHUN(mEntry);
-
- given(mHeadsUpManager.canRemoveImmediately(anyString())).willAnswer(i -> {
- String key = i.getArgument(0);
- for (NotificationEntry entry : mHuns) {
- if (entry.getKey().equals(key)) return false;
- }
- return true;
- });
- // THEN only the current HUN, mEntry, should be lifetimeExtended
- assertTrue(mNotifLifetimeExtender.maybeExtendLifetime(mEntry, /* cancellationReason */ 0));
- assertFalse(mNotifLifetimeExtender.maybeExtendLifetime(
- new NotificationEntryBuilder()
- .setPkg("test-package")
- .build(), /* cancellationReason */ 0));
- }
-
- @Test
- public void testShowHUNOnInflationFinished() {
- // WHEN a notification should HUN and its inflation is finished
- when(mNotificationInterruptStateProvider.shouldHeadsUp(mEntry)).thenReturn(true);
-
- ArgumentCaptor<BindCallback> bindCallbackCaptor =
- ArgumentCaptor.forClass(BindCallback.class);
- mCollectionListener.onEntryAdded(mEntry);
- verify(mHeadsUpViewBinder).bindHeadsUpView(eq(mEntry), bindCallbackCaptor.capture());
-
- bindCallbackCaptor.getValue().onBindFinished(mEntry);
-
- // THEN we tell the HeadsUpManager to show the notification
- verify(mHeadsUpManager).showNotification(mEntry);
- }
-
- @Test
- public void testNoHUNOnInflationFinished() {
- // WHEN a notification shouldn't HUN and its inflation is finished
- when(mNotificationInterruptStateProvider.shouldHeadsUp(mEntry)).thenReturn(false);
- ArgumentCaptor<BindCallback> bindCallbackCaptor =
- ArgumentCaptor.forClass(BindCallback.class);
- mCollectionListener.onEntryAdded(mEntry);
-
- // THEN we never bind the heads up view or tell HeadsUpManager to show the notification
- verify(mHeadsUpViewBinder, never()).bindHeadsUpView(
- eq(mEntry), bindCallbackCaptor.capture());
- verify(mHeadsUpManager, never()).showNotification(mEntry);
- }
-
- @Test
- public void testOnEntryRemovedRemovesHeadsUpNotification() {
- // GIVEN the current HUN is mEntry
- addHUN(mEntry);
-
- // WHEN mEntry is removed from the notification collection
- mCollectionListener.onEntryRemoved(mEntry, /* cancellation reason */ 0);
- when(mRemoteInputManager.isSpinning(any())).thenReturn(false);
-
- // THEN heads up manager should remove the entry
- verify(mHeadsUpManager).removeNotification(mEntry.getKey(), false);
- }
-
- private void addHUN(NotificationEntry entry) {
- mHuns.add(entry);
- when(mHeadsUpManager.getTopEntry()).thenReturn(entry);
- mOnHeadsUpChangedListener.onHeadsUpStateChanged(entry, entry != null);
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt
new file mode 100644
index 000000000000..c67a2331b023
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt
@@ -0,0 +1,238 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar.notification.collection.coordinator
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.NotificationRemoteInputManager
+import com.android.systemui.statusbar.notification.collection.NotifPipeline
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender.OnEndLifetimeExtensionCallback
+import com.android.systemui.statusbar.notification.collection.render.NodeController
+import com.android.systemui.statusbar.notification.interruption.HeadsUpViewBinder
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider
+import com.android.systemui.statusbar.notification.row.NotifBindPipeline.BindCallback
+import com.android.systemui.statusbar.policy.HeadsUpManager
+import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.withArgCaptor
+import com.android.systemui.util.time.FakeSystemClock
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyString
+import org.mockito.BDDMockito.given
+import org.mockito.Mockito.never
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+import java.util.ArrayList
+import org.mockito.Mockito.`when` as whenever
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
+class HeadsUpCoordinatorTest : SysuiTestCase() {
+ private lateinit var mCoordinator: HeadsUpCoordinator
+
+ // captured listeners and pluggables:
+ private lateinit var mCollectionListener: NotifCollectionListener
+ private lateinit var mNotifPromoter: NotifPromoter
+ private lateinit var mNotifLifetimeExtender: NotifLifetimeExtender
+ private lateinit var mOnHeadsUpChangedListener: OnHeadsUpChangedListener
+ private lateinit var mNotifSectioner: NotifSectioner
+
+ private val mNotifPipeline: NotifPipeline = mock()
+ private val mHeadsUpManager: HeadsUpManager = mock()
+ private val mHeadsUpViewBinder: HeadsUpViewBinder = mock()
+ private val mNotificationInterruptStateProvider: NotificationInterruptStateProvider = mock()
+ private val mRemoteInputManager: NotificationRemoteInputManager = mock()
+ private val mEndLifetimeExtension: OnEndLifetimeExtensionCallback = mock()
+ private val mHeaderController: NodeController = mock()
+
+ private lateinit var mEntry: NotificationEntry
+ private val mExecutor = FakeExecutor(FakeSystemClock())
+ private val mHuns: ArrayList<NotificationEntry> = ArrayList()
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ mCoordinator = HeadsUpCoordinator(
+ mHeadsUpManager,
+ mHeadsUpViewBinder,
+ mNotificationInterruptStateProvider,
+ mRemoteInputManager,
+ mHeaderController,
+ mExecutor)
+ mCoordinator.attach(mNotifPipeline)
+
+ // capture arguments:
+ mCollectionListener = withArgCaptor {
+ verify(mNotifPipeline).addCollectionListener(capture())
+ }
+ mNotifPromoter = withArgCaptor {
+ verify(mNotifPipeline).addPromoter(capture())
+ }
+ mNotifLifetimeExtender = withArgCaptor {
+ verify(mNotifPipeline).addNotificationLifetimeExtender(capture())
+ }
+ mOnHeadsUpChangedListener = withArgCaptor {
+ verify(mHeadsUpManager).addListener(capture())
+ }
+ given(mHeadsUpManager.allEntries).willAnswer { mHuns.stream() }
+ given(mHeadsUpManager.isAlerting(anyString())).willAnswer { invocation ->
+ val key = invocation.getArgument<String>(0)
+ mHuns.any { entry -> entry.key == key }
+ }
+ given(mHeadsUpManager.canRemoveImmediately(anyString())).willAnswer { invocation ->
+ val key = invocation.getArgument<String>(0)
+ !mHuns.any { entry -> entry.key == key }
+ }
+ whenever(mHeadsUpManager.getEarliestRemovalTime(anyString())).thenReturn(1000L)
+ mNotifSectioner = mCoordinator.sectioner
+ mNotifLifetimeExtender.setCallback(mEndLifetimeExtension)
+ mEntry = NotificationEntryBuilder().build()
+ }
+
+ @Test
+ fun testCancelStickyNotification() {
+ whenever(mHeadsUpManager.isSticky(anyString())).thenReturn(true)
+ addHUN(mEntry)
+ whenever(mHeadsUpManager.canRemoveImmediately(anyString())).thenReturn(false, true)
+ whenever(mHeadsUpManager.getEarliestRemovalTime(anyString())).thenReturn(1000L, 0L)
+ assertTrue(mNotifLifetimeExtender.maybeExtendLifetime(mEntry, 0))
+ mExecutor.advanceClockToLast()
+ mExecutor.runAllReady()
+ verify(mHeadsUpManager, times(0)).removeNotification(anyString(), eq(false))
+ verify(mHeadsUpManager, times(1)).removeNotification(anyString(), eq(true))
+ }
+
+ @Test
+ fun testCancelUpdatedStickyNotification() {
+ whenever(mHeadsUpManager.isSticky(anyString())).thenReturn(true)
+ addHUN(mEntry)
+ whenever(mHeadsUpManager.getEarliestRemovalTime(anyString())).thenReturn(1000L, 500L)
+ assertTrue(mNotifLifetimeExtender.maybeExtendLifetime(mEntry, 0))
+ mExecutor.advanceClockToLast()
+ mExecutor.runAllReady()
+ verify(mHeadsUpManager, times(0)).removeNotification(anyString(), eq(false))
+ verify(mHeadsUpManager, times(0)).removeNotification(anyString(), eq(true))
+ }
+
+ @Test
+ fun testCancelNotification() {
+ whenever(mHeadsUpManager.isSticky(anyString())).thenReturn(false)
+ addHUN(mEntry)
+ whenever(mHeadsUpManager.getEarliestRemovalTime(anyString())).thenReturn(1000L, 500L)
+ assertTrue(mNotifLifetimeExtender.maybeExtendLifetime(mEntry, 0))
+ mExecutor.advanceClockToLast()
+ mExecutor.runAllReady()
+ verify(mHeadsUpManager, times(1)).removeNotification(anyString(), eq(false))
+ verify(mHeadsUpManager, times(0)).removeNotification(anyString(), eq(true))
+ }
+
+ @Test
+ fun testPromotesCurrentHUN() {
+ // GIVEN the current HUN is set to mEntry
+ addHUN(mEntry)
+
+ // THEN only promote the current HUN, mEntry
+ assertTrue(mNotifPromoter.shouldPromoteToTopLevel(mEntry))
+ assertFalse(mNotifPromoter.shouldPromoteToTopLevel(NotificationEntryBuilder()
+ .setPkg("test-package2")
+ .build()))
+ }
+
+ @Test
+ fun testIncludeInSectionCurrentHUN() {
+ // GIVEN the current HUN is set to mEntry
+ addHUN(mEntry)
+
+ // THEN only section the current HUN, mEntry
+ assertTrue(mNotifSectioner.isInSection(mEntry))
+ assertFalse(mNotifSectioner.isInSection(NotificationEntryBuilder()
+ .setPkg("test-package")
+ .build()))
+ }
+
+ @Test
+ fun testLifetimeExtendsCurrentHUN() {
+ // GIVEN there is a HUN, mEntry
+ addHUN(mEntry)
+
+ // THEN only the current HUN, mEntry, should be lifetimeExtended
+ assertTrue(mNotifLifetimeExtender.maybeExtendLifetime(mEntry, /* cancellationReason */ 0))
+ assertFalse(mNotifLifetimeExtender.maybeExtendLifetime(
+ NotificationEntryBuilder()
+ .setPkg("test-package")
+ .build(), /* cancellationReason */ 0))
+ }
+
+ @Test
+ fun testShowHUNOnInflationFinished() {
+ // WHEN a notification should HUN and its inflation is finished
+ whenever(mNotificationInterruptStateProvider.shouldHeadsUp(mEntry)).thenReturn(true)
+
+ mCollectionListener.onEntryAdded(mEntry)
+ withArgCaptor<BindCallback> {
+ verify(mHeadsUpViewBinder).bindHeadsUpView(eq(mEntry), capture())
+ }.onBindFinished(mEntry)
+
+ // THEN we tell the HeadsUpManager to show the notification
+ verify(mHeadsUpManager).showNotification(mEntry)
+ }
+
+ @Test
+ fun testNoHUNOnInflationFinished() {
+ // WHEN a notification shouldn't HUN and its inflation is finished
+ whenever(mNotificationInterruptStateProvider.shouldHeadsUp(mEntry)).thenReturn(false)
+ mCollectionListener.onEntryAdded(mEntry)
+
+ // THEN we never bind the heads up view or tell HeadsUpManager to show the notification
+ verify(mHeadsUpViewBinder, never()).bindHeadsUpView(eq(mEntry), any())
+ verify(mHeadsUpManager, never()).showNotification(mEntry)
+ }
+
+ @Test
+ fun testOnEntryRemovedRemovesHeadsUpNotification() {
+ // GIVEN the current HUN is mEntry
+ addHUN(mEntry)
+
+ // WHEN mEntry is removed from the notification collection
+ mCollectionListener.onEntryRemoved(mEntry, /* cancellation reason */ 0)
+ whenever(mRemoteInputManager.isSpinning(any())).thenReturn(false)
+
+ // THEN heads up manager should remove the entry
+ verify(mHeadsUpManager).removeNotification(mEntry.key, false)
+ }
+
+ private fun addHUN(entry: NotificationEntry) {
+ mHuns.add(entry)
+ whenever(mHeadsUpManager.topEntry).thenReturn(entry)
+ mOnHeadsUpChangedListener.onHeadsUpStateChanged(entry, true)
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java
index 917c049fd578..d0947497f0ec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java
@@ -41,6 +41,7 @@ import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.RankingBuilder;
+import com.android.systemui.statusbar.notification.SectionHeaderVisibilityProvider;
import com.android.systemui.statusbar.notification.collection.GroupEntry;
import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
@@ -70,6 +71,7 @@ public class KeyguardCoordinatorTest extends SysuiTestCase {
@Mock private StatusBarStateController mStatusBarStateController;
@Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@Mock private HighPriorityProvider mHighPriorityProvider;
+ @Mock private SectionHeaderVisibilityProvider mSectionHeaderVisibilityProvider;
@Mock private NotifPipeline mNotifPipeline;
private NotificationEntry mEntry;
@@ -81,7 +83,7 @@ public class KeyguardCoordinatorTest extends SysuiTestCase {
KeyguardCoordinator keyguardCoordinator = new KeyguardCoordinator(
mContext, mMainHandler, mKeyguardStateController, mLockscreenUserManager,
mBroadcastDispatcher, mStatusBarStateController,
- mKeyguardUpdateMonitor, mHighPriorityProvider);
+ mKeyguardUpdateMonitor, mHighPriorityProvider, mSectionHeaderVisibilityProvider);
mEntry = new NotificationEntryBuilder()
.setUser(new UserHandle(NOTIF_USER_ID))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java
index bde6734bbf92..3b034f7af9a6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java
@@ -26,6 +26,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import static java.util.Objects.requireNonNull;
@@ -40,7 +41,6 @@ import androidx.test.filters.SmallTest;
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.RankingBuilder;
-import com.android.systemui.statusbar.notification.ConversationNotificationManager;
import com.android.systemui.statusbar.notification.SectionClassifier;
import com.android.systemui.statusbar.notification.collection.GroupEntry;
import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder;
@@ -48,6 +48,7 @@ import com.android.systemui.statusbar.notification.collection.ListEntry;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
+import com.android.systemui.statusbar.notification.collection.inflation.BindEventManagerImpl;
import com.android.systemui.statusbar.notification.collection.inflation.NotifInflater;
import com.android.systemui.statusbar.notification.collection.inflation.NotifUiAdjustmentProvider;
import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection;
@@ -93,7 +94,7 @@ public class PreparationCoordinatorTest extends SysuiTestCase {
@Mock private NotifSection mNotifSection;
@Mock private NotifPipeline mNotifPipeline;
@Mock private IStatusBarService mService;
- @Mock private ConversationNotificationManager mConvoManager;
+ @Mock private BindEventManagerImpl mBindEventManagerImpl;
@Spy private FakeNotifInflater mNotifInflater = new FakeNotifInflater();
private final SectionClassifier mSectionClassifier = new SectionClassifier();
private final NotifUiAdjustmentProvider mAdjustmentProvider =
@@ -121,7 +122,7 @@ public class PreparationCoordinatorTest extends SysuiTestCase {
mock(NotifViewBarn.class),
mAdjustmentProvider,
mService,
- mConvoManager,
+ mBindEventManagerImpl,
TEST_CHILD_BIND_CUTOFF,
TEST_MAX_GROUP_DELAY);
@@ -411,7 +412,8 @@ public class PreparationCoordinatorTest extends SysuiTestCase {
public void testCallConversationManagerBindWhenInflated() {
mBeforeFilterListener.onBeforeFinalizeFilter(List.of(mEntry));
mNotifInflater.getInflateCallback(mEntry).onInflationFinished(mEntry, null);
- verify(mConvoManager, times(1)).onEntryViewBound(eq(mEntry));
+ verify(mBindEventManagerImpl, times(1)).notifyViewBound(eq(mEntry));
+ verifyNoMoreInteractions(mBindEventManagerImpl);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt
index f77381000ae2..4e309d49c6d2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt
@@ -19,6 +19,7 @@ package com.android.systemui.statusbar.notification.collection.render
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
+import com.android.systemui.statusbar.notification.SectionHeaderVisibilityProvider
import com.android.systemui.statusbar.notification.collection.GroupEntry
import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder
import com.android.systemui.statusbar.notification.collection.ListEntry
@@ -43,6 +44,7 @@ class NodeSpecBuilderTest : SysuiTestCase() {
private val mediaContainerController: MediaContainerController = mock()
private val sectionsFeatureManager: NotificationSectionsFeatureManager = mock()
+ private val sectionHeaderVisibilityProvider: SectionHeaderVisibilityProvider = mock()
private val viewBarn: NotifViewBarn = mock()
private var rootController: NodeController = buildFakeController("rootController")
@@ -72,11 +74,13 @@ class NodeSpecBuilderTest : SysuiTestCase() {
fakeViewBarn.getViewByEntry(it.getArgument(0))
}
- specBuilder = NodeSpecBuilder(mediaContainerController, sectionsFeatureManager, viewBarn)
+ specBuilder = NodeSpecBuilder(mediaContainerController, sectionsFeatureManager,
+ sectionHeaderVisibilityProvider, viewBarn)
}
@Test
fun testMultipleSectionsWithSameController() {
+ whenever(sectionHeaderVisibilityProvider.sectionHeadersVisible).thenReturn(true)
checkOutput(
listOf(
notif(0, section0),
@@ -95,6 +99,7 @@ class NodeSpecBuilderTest : SysuiTestCase() {
@Test(expected = RuntimeException::class)
fun testMultipleSectionsWithSameControllerNonConsecutive() {
+ whenever(sectionHeaderVisibilityProvider.sectionHeadersVisible).thenReturn(true)
checkOutput(
listOf(
notif(0, section0),
@@ -108,6 +113,7 @@ class NodeSpecBuilderTest : SysuiTestCase() {
@Test
fun testSimpleMapping() {
+ whenever(sectionHeaderVisibilityProvider.sectionHeadersVisible).thenReturn(true)
checkOutput(
// GIVEN a simple flat list of notifications all in the same headerless section
listOf(
@@ -129,6 +135,7 @@ class NodeSpecBuilderTest : SysuiTestCase() {
@Test
fun testSimpleMappingWithMedia() {
+ whenever(sectionHeaderVisibilityProvider.sectionHeadersVisible).thenReturn(true)
// WHEN media controls are enabled
whenever(sectionsFeatureManager.isMediaControlsEnabled()).thenReturn(true)
@@ -154,6 +161,8 @@ class NodeSpecBuilderTest : SysuiTestCase() {
@Test
fun testHeaderInjection() {
+ // WHEN section headers are supposed to be visible
+ whenever(sectionHeaderVisibilityProvider.sectionHeadersVisible).thenReturn(true)
checkOutput(
// GIVEN a flat list of notifications, spread across three sections
listOf(
@@ -177,7 +186,31 @@ class NodeSpecBuilderTest : SysuiTestCase() {
}
@Test
+ fun testHeaderSuppression() {
+ // WHEN section headers are supposed to be hidden
+ whenever(sectionHeaderVisibilityProvider.sectionHeadersVisible).thenReturn(false)
+ checkOutput(
+ // GIVEN a flat list of notifications, spread across three sections
+ listOf(
+ notif(0, section0),
+ notif(1, section0),
+ notif(2, section1),
+ notif(3, section2)
+ ),
+
+ // THEN each section has its header injected
+ tree(
+ notifNode(0),
+ notifNode(1),
+ notifNode(2),
+ notifNode(3)
+ )
+ )
+ }
+
+ @Test
fun testGroups() {
+ whenever(sectionHeaderVisibilityProvider.sectionHeadersVisible).thenReturn(true)
checkOutput(
// GIVEN a mixed list of top-level notifications and groups
listOf(
@@ -218,6 +251,7 @@ class NodeSpecBuilderTest : SysuiTestCase() {
@Test
fun testSecondSectionWithNoHeader() {
+ whenever(sectionHeaderVisibilityProvider.sectionHeadersVisible).thenReturn(true)
checkOutput(
// GIVEN a middle section with no associated header view
listOf(
@@ -247,6 +281,7 @@ class NodeSpecBuilderTest : SysuiTestCase() {
@Test(expected = RuntimeException::class)
fun testRepeatedSectionsThrow() {
+ whenever(sectionHeaderVisibilityProvider.sectionHeadersVisible).thenReturn(true)
checkOutput(
// GIVEN a malformed list where sections are not contiguous
listOf(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferTest.java
index bbe92f67ca2e..15ff5551703b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferTest.java
@@ -274,6 +274,18 @@ public class ShadeViewDifferTest extends SysuiTestCase {
public void removeChild(@NonNull NodeController child, boolean isTransfer) {
view.removeView(child.getView());
}
+
+ @Override
+ public void onViewAdded() {
+ }
+
+ @Override
+ public void onViewMoved() {
+ }
+
+ @Override
+ public void onViewRemoved() {
+ }
}
private static class SpecBuilder {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
index c3349f1d70f4..5ca1f21eb021 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
@@ -42,6 +42,7 @@ import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.WakefulnessLifecycle;
@@ -104,6 +105,8 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase {
private ScreenLifecycle mScreenLifecycle;
@Mock
private StatusBarStateController mStatusBarStateController;
+ @Mock
+ private KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
private BiometricUnlockController mBiometricUnlockController;
@Before
@@ -126,7 +129,7 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase {
mUpdateMonitor, res.getResources(), mKeyguardBypassController, mDozeParameters,
mMetricsLogger, mDumpManager, mPowerManager,
mNotificationMediaManager, mWakefulnessLifecycle, mScreenLifecycle,
- mAuthController, mStatusBarStateController);
+ mAuthController, mStatusBarStateController, mKeyguardUnlockAnimationController);
mBiometricUnlockController.setKeyguardViewController(mStatusBarKeyguardViewManager);
mBiometricUnlockController.setBiometricModeListener(mBiometricModeListener);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
index 35f671bf8298..1c8b35aeaeaf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
@@ -104,6 +104,7 @@ import com.android.systemui.fragments.FragmentHostManager;
import com.android.systemui.fragments.FragmentService;
import com.android.systemui.idle.IdleHostViewController;
import com.android.systemui.idle.dagger.IdleViewComponent;
+import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.media.KeyguardMediaController;
import com.android.systemui.media.MediaDataManager;
import com.android.systemui.media.MediaHierarchyManager;
@@ -362,6 +363,8 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
private QsFrameTranslateController mQsFrameTranslateController;
@Mock
private StatusBarWindowStateController mStatusBarWindowStateController;
+ @Mock
+ private KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
private Optional<SysUIUnfoldComponent> mSysUIUnfoldComponent = Optional.empty();
private SysuiStatusBarStateController mStatusBarStateController;
private NotificationPanelViewController mNotificationPanelViewController;
@@ -546,7 +549,8 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
mSysUIUnfoldComponent,
mControlsComponent,
mInteractionJankMonitor,
- mQsFrameTranslateController);
+ mQsFrameTranslateController,
+ mKeyguardUnlockAnimationController);
mNotificationPanelViewController.initDependencies(
mStatusBar,
() -> {},
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 f21fca23df5b..10f4435d3f97 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
@@ -647,8 +647,15 @@ public class ScrimControllerTest extends SysuiTestCase {
mScrimController.setRawPanelExpansionFraction(0.3f);
assertScrimAlpha(Map.of(
mScrimInFront, TRANSPARENT,
- mNotificationsScrim, SEMI_TRANSPARENT,
+ mNotificationsScrim, TRANSPARENT,
mScrimBehind, SEMI_TRANSPARENT));
+
+ // Then, notification scrim should fade in
+ mScrimController.setRawPanelExpansionFraction(0.7f);
+ assertScrimAlpha(Map.of(
+ mScrimInFront, TRANSPARENT,
+ mNotificationsScrim, SEMI_TRANSPARENT,
+ mScrimBehind, OPAQUE));
}
@@ -1132,6 +1139,7 @@ public class ScrimControllerTest extends SysuiTestCase {
@Test
public void testScrimsVisible_whenShadeVisible() {
+ mScrimController.setClipsQsScrim(true);
mScrimController.transitionTo(ScrimState.UNLOCKED);
mScrimController.setRawPanelExpansionFraction(0.3f);
// notifications scrim alpha change require calling setQsPosition
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 bb8bad39ab31..77065b2d4380 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
@@ -819,31 +819,27 @@ public class StatusBarTest extends SysuiTestCase {
}
@Test
- public void testSetExpansionAffectsAlpha_onlyWhenHidingKeyguard() {
+ public void testSetExpansionAffectsAlpha_whenKeyguardShowingButGoingAwayForAnyReason() {
mStatusBar.updateScrimController();
verify(mScrimController).setExpansionAffectsAlpha(eq(true));
clearInvocations(mScrimController);
- when(mBiometricUnlockController.isBiometricUnlock()).thenReturn(true);
+ when(mKeyguardStateController.isShowing()).thenReturn(true);
+ when(mKeyguardStateController.isKeyguardGoingAway()).thenReturn(false);
mStatusBar.updateScrimController();
verify(mScrimController).setExpansionAffectsAlpha(eq(true));
clearInvocations(mScrimController);
when(mKeyguardStateController.isShowing()).thenReturn(true);
+ when(mKeyguardStateController.isKeyguardGoingAway()).thenReturn(true);
mStatusBar.updateScrimController();
verify(mScrimController).setExpansionAffectsAlpha(eq(false));
clearInvocations(mScrimController);
- reset(mKeyguardStateController);
+ when(mKeyguardStateController.isShowing()).thenReturn(true);
when(mKeyguardStateController.isKeyguardFadingAway()).thenReturn(true);
mStatusBar.updateScrimController();
verify(mScrimController).setExpansionAffectsAlpha(eq(false));
-
- clearInvocations(mScrimController);
- reset(mKeyguardStateController);
- when(mKeyguardStateController.isKeyguardGoingAway()).thenReturn(true);
- mStatusBar.updateScrimController();
- verify(mScrimController).setExpansionAffectsAlpha(eq(false));
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt
index 24a56bc76fae..71b32c0bd106 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt
@@ -17,6 +17,8 @@
package com.android.systemui.statusbar.phone
import android.animation.Animator
+import android.os.Handler
+import android.os.PowerManager
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import android.view.View
@@ -29,13 +31,19 @@ import com.android.systemui.statusbar.LightRevealScrim
import com.android.systemui.statusbar.StatusBarStateControllerImpl
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.settings.GlobalSettings
+import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.Mock
import org.mockito.Mockito
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.anyLong
+import org.mockito.Mockito.never
import org.mockito.Mockito.spy
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@SmallTest
@@ -53,7 +61,9 @@ class UnlockedScreenOffAnimationControllerTest : SysuiTestCase() {
@Mock
private lateinit var globalSettings: GlobalSettings
@Mock
- private lateinit var statusbar: StatusBar
+ private lateinit var statusBar: StatusBar
+ @Mock
+ private lateinit var notificationPanelViewController: NotificationPanelViewController
@Mock
private lateinit var lightRevealScrim: LightRevealScrim
@Mock
@@ -62,6 +72,10 @@ class UnlockedScreenOffAnimationControllerTest : SysuiTestCase() {
private lateinit var statusBarStateController: StatusBarStateControllerImpl
@Mock
private lateinit var interactionJankMonitor: InteractionJankMonitor
+ @Mock
+ private lateinit var powerManager: PowerManager
+ @Mock
+ private lateinit var handler: Handler
@Before
fun setUp() {
@@ -75,9 +89,24 @@ class UnlockedScreenOffAnimationControllerTest : SysuiTestCase() {
keyguardStateController,
dagger.Lazy<DozeParameters> { dozeParameters },
globalSettings,
- interactionJankMonitor
+ interactionJankMonitor,
+ powerManager,
+ handler = handler
)
- controller.initialize(statusbar, lightRevealScrim)
+ controller.initialize(statusBar, lightRevealScrim)
+ `when`(statusBar.notificationPanelViewController).thenReturn(
+ notificationPanelViewController)
+
+ // Screen off does not run if the panel is expanded, so we should say it's collapsed to test
+ // screen off.
+ `when`(notificationPanelViewController.isFullyCollapsed).thenReturn(true)
+ }
+
+ @After
+ fun cleanUp() {
+ // Tell the screen off controller to cancel the animations and clean up its state, or
+ // subsequent tests will act unpredictably as the animator continues running.
+ controller.onStartedWakingUp()
}
@Test
@@ -93,4 +122,49 @@ class UnlockedScreenOffAnimationControllerTest : SysuiTestCase() {
listener.value.onAnimationEnd(null)
Mockito.verify(animator).setListener(null)
}
+
+ /**
+ * The AOD UI is shown during the screen off animation, after a delay to allow the light reveal
+ * animation to start. If the device is woken up during the screen off, we should *never* do
+ * this.
+ *
+ * This test confirms that we do show the AOD UI when the device is not woken up
+ * (PowerManager#isInteractive = false).
+ */
+ @Test
+ fun testAodUiShownIfNotInteractive() {
+ `when`(dozeParameters.shouldControlUnlockedScreenOff()).thenReturn(true)
+ `when`(powerManager.isInteractive).thenReturn(false)
+
+ val callbackCaptor = ArgumentCaptor.forClass(Runnable::class.java)
+ controller.startAnimation()
+
+ verify(handler).postDelayed(callbackCaptor.capture(), anyLong())
+
+ callbackCaptor.value.run()
+
+ verify(notificationPanelViewController, times(1)).showAodUi()
+ }
+
+ /**
+ * The AOD UI is shown during the screen off animation, after a delay to allow the light reveal
+ * animation to start. If the device is woken up during the screen off, we should *never* do
+ * this.
+ *
+ * This test confirms that we do not show the AOD UI when the device is woken up during screen
+ * off (PowerManager#isInteractive = true).
+ */
+ @Test
+ fun testAodUiNotShownIfInteractive() {
+ `when`(dozeParameters.shouldControlUnlockedScreenOff()).thenReturn(true)
+ `when`(powerManager.isInteractive).thenReturn(true)
+
+ val callbackCaptor = ArgumentCaptor.forClass(Runnable::class.java)
+ controller.startAnimation()
+
+ verify(handler).postDelayed(callbackCaptor.capture(), anyLong())
+ callbackCaptor.value.run()
+
+ verify(notificationPanelViewController, never()).showAodUi()
+ }
} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java
index a8522c787029..6364d2f23299 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java
@@ -52,13 +52,14 @@ import org.mockito.MockitoAnnotations;
@SmallTest
public class DeviceStateRotationLockSettingControllerTest extends SysuiTestCase {
- private static final String[] DEFAULT_SETTINGS = new String[] {"0:0", "1:2"};
+ private static final String[] DEFAULT_SETTINGS = new String[]{"0:1", "2:0:1", "1:2"};
private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
private final FakeExecutor mFakeExecutor = new FakeExecutor(mFakeSystemClock);
- @Mock DeviceStateManager mDeviceStateManager;
- RotationPolicyWrapper mFakeRotationPolicy = new FakeRotationPolicy();
- DeviceStateRotationLockSettingController mDeviceStateRotationLockSettingController;
+ @Mock
+ private DeviceStateManager mDeviceStateManager;
+ private final RotationPolicyWrapper mFakeRotationPolicy = new FakeRotationPolicy();
+ private DeviceStateRotationLockSettingController mDeviceStateRotationLockSettingController;
private DeviceStateManager.DeviceStateCallback mDeviceStateCallback;
private DeviceStateRotationLockSettingsManager mSettingsManager;
private TestableContentResolver mContentResolver;
@@ -93,7 +94,7 @@ public class DeviceStateRotationLockSettingControllerTest extends SysuiTestCase
mContentResolver,
Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
UserHandle.USER_CURRENT))
- .isEqualTo("0:0:1:2");
+ .isEqualTo("0:1:1:2:2:0");
}
@Test
@@ -125,6 +126,31 @@ public class DeviceStateRotationLockSettingControllerTest extends SysuiTestCase
}
@Test
+ public void whenDeviceStateSwitched_settingIsIgnored_loadsDefaultFallbackSetting() {
+ initializeSettingsWith();
+ mFakeRotationPolicy.setRotationLock(true);
+
+ // State 2 -> Ignored -> Fall back to state 1 which is unlocked
+ mDeviceStateCallback.onStateChanged(2);
+
+ assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
+ }
+
+ @Test
+ public void whenDeviceStateSwitched_ignoredSetting_fallbackValueChanges_usesFallbackValue() {
+ initializeSettingsWith(
+ 0, DEVICE_STATE_ROTATION_LOCK_UNLOCKED,
+ 1, DEVICE_STATE_ROTATION_LOCK_LOCKED,
+ 2, DEVICE_STATE_ROTATION_LOCK_IGNORED);
+ mFakeRotationPolicy.setRotationLock(false);
+
+ // State 2 -> Ignored -> Fall back to state 1 which is locked
+ mDeviceStateCallback.onStateChanged(2);
+
+ assertThat(mFakeRotationPolicy.isRotationLocked()).isTrue();
+ }
+
+ @Test
public void whenUserChangesSetting_saveSettingForCurrentState() {
initializeSettingsWith(
0, DEVICE_STATE_ROTATION_LOCK_LOCKED, 1, DEVICE_STATE_ROTATION_LOCK_UNLOCKED);
@@ -159,15 +185,15 @@ public class DeviceStateRotationLockSettingControllerTest extends SysuiTestCase
}
@Test
- public void whenDeviceStateSwitchedToIgnoredState_newSettingsSaveForPreviousState() {
+ public void whenDeviceStateSwitchedToIgnoredState_noFallback_newSettingsSaveForPreviousState() {
initializeSettingsWith(
- 0, DEVICE_STATE_ROTATION_LOCK_IGNORED, 1, DEVICE_STATE_ROTATION_LOCK_UNLOCKED);
+ 8, DEVICE_STATE_ROTATION_LOCK_IGNORED, 1, DEVICE_STATE_ROTATION_LOCK_UNLOCKED);
mFakeRotationPolicy.setRotationLock(true);
mDeviceStateCallback.onStateChanged(1);
assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
- mDeviceStateCallback.onStateChanged(0);
+ mDeviceStateCallback.onStateChanged(8);
assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
mDeviceStateRotationLockSettingController.onRotationLockStateChanged(
@@ -178,7 +204,7 @@ public class DeviceStateRotationLockSettingControllerTest extends SysuiTestCase
mContentResolver,
Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
UserHandle.USER_CURRENT))
- .isEqualTo("0:0:1:1");
+ .isEqualTo("1:1:8:0");
}
@Test
@@ -198,12 +224,78 @@ public class DeviceStateRotationLockSettingControllerTest extends SysuiTestCase
assertThat(mFakeRotationPolicy.isRotationLocked()).isTrue();
}
+ @Test
+ public void onRotationLockStateChanged_newSettingIsPersisted() {
+ initializeSettingsWith(
+ 0, DEVICE_STATE_ROTATION_LOCK_LOCKED,
+ 1, DEVICE_STATE_ROTATION_LOCK_UNLOCKED);
+ mDeviceStateCallback.onStateChanged(0);
+
+ mDeviceStateRotationLockSettingController.onRotationLockStateChanged(
+ /* rotationLocked= */ false,
+ /* affordanceVisible= */ true
+ );
+
+ assertThat(
+ Settings.Secure.getStringForUser(
+ mContentResolver,
+ Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
+ UserHandle.USER_CURRENT))
+ .isEqualTo("0:2:1:2");
+ }
+
+ @Test
+ public void onRotationLockStateChanged_deviceStateIsIgnored_newSettingIsPersistedToFallback() {
+ initializeSettingsWith(
+ 0, DEVICE_STATE_ROTATION_LOCK_LOCKED,
+ 1, DEVICE_STATE_ROTATION_LOCK_UNLOCKED,
+ 2, DEVICE_STATE_ROTATION_LOCK_IGNORED);
+ mDeviceStateCallback.onStateChanged(2);
+
+ mDeviceStateRotationLockSettingController.onRotationLockStateChanged(
+ /* rotationLocked= */ true,
+ /* affordanceVisible= */ true
+ );
+
+ assertThat(
+ Settings.Secure.getStringForUser(
+ mContentResolver,
+ Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
+ UserHandle.USER_CURRENT))
+ .isEqualTo("0:1:1:1:2:0");
+ }
+
+ @Test
+ public void onRotationLockStateChange_stateIgnored_noFallback_settingIsPersistedToPrevious() {
+ initializeSettingsWith(
+ 0, DEVICE_STATE_ROTATION_LOCK_LOCKED,
+ 1, DEVICE_STATE_ROTATION_LOCK_UNLOCKED,
+ 8, DEVICE_STATE_ROTATION_LOCK_IGNORED);
+ mDeviceStateCallback.onStateChanged(1);
+ mDeviceStateCallback.onStateChanged(8);
+
+ mDeviceStateRotationLockSettingController.onRotationLockStateChanged(
+ /* rotationLocked= */ true,
+ /* affordanceVisible= */ true
+ );
+
+ assertThat(
+ Settings.Secure.getStringForUser(
+ mContentResolver,
+ Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
+ UserHandle.USER_CURRENT))
+ .isEqualTo("0:1:1:1:8:0");
+ }
+
private void initializeSettingsWith(int... values) {
if (values.length % 2 != 0) {
throw new IllegalArgumentException("Expecting key-value pairs");
}
StringBuilder sb = new StringBuilder();
- for (int i = 0; i < values.length; sb.append(":")) {
+ for (int i = 0; i < values.length; ) {
+ if (i > 0) {
+ sb.append(":");
+ }
sb.append(values[i++]).append(":").append(values[i++]);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java
index 8ccaf9362454..4a8170fc2955 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java
@@ -33,7 +33,7 @@ import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController;
+import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import org.junit.Before;
import org.junit.Test;
@@ -41,6 +41,8 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import dagger.Lazy;
+
@SmallTest
@TestableLooper.RunWithLooper
@RunWith(AndroidTestingRunner.class)
@@ -52,9 +54,9 @@ public class KeyguardStateControllerTest extends SysuiTestCase {
private LockPatternUtils mLockPatternUtils;
private KeyguardStateController mKeyguardStateController;
@Mock
- private SmartspaceTransitionController mSmartSpaceTransitionController;
- @Mock
private DumpManager mDumpManager;
+ @Mock
+ private Lazy<KeyguardUnlockAnimationController> mKeyguardUnlockAnimationControllerLazy;
@Before
public void setup() {
@@ -63,7 +65,7 @@ public class KeyguardStateControllerTest extends SysuiTestCase {
mContext,
mKeyguardUpdateMonitor,
mLockPatternUtils,
- mSmartSpaceTransitionController,
+ mKeyguardUnlockAnimationControllerLazy,
mDumpManager);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java
index 087f2e6006cf..2126dda7b310 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java
@@ -22,6 +22,7 @@ import static org.mockito.Mockito.when;
import android.app.AppOpsManager;
import android.content.Intent;
+import android.content.pm.UserInfo;
import android.location.LocationManager;
import android.os.Handler;
import android.os.UserHandle;
@@ -68,6 +69,8 @@ public class LocationControllerImplTest extends SysuiTestCase {
MockitoAnnotations.initMocks(this);
when(mUserTracker.getUserId()).thenReturn(UserHandle.USER_SYSTEM);
when(mUserTracker.getUserHandle()).thenReturn(UserHandle.SYSTEM);
+ when(mUserTracker.getUserProfiles())
+ .thenReturn(ImmutableList.of(new UserInfo(0, "name", 0)));
mDeviceConfigProxy = new DeviceConfigProxyFake();
mTestableLooper = TestableLooper.get(this);
@@ -78,7 +81,8 @@ public class LocationControllerImplTest extends SysuiTestCase {
new Handler(mTestableLooper.getLooper()),
mock(BroadcastDispatcher.class),
mock(BootCompleteCache.class),
- mUserTracker);
+ mUserTracker,
+ mContext.getPackageManager());
mTestableLooper.processAllMessages();
}
@@ -161,17 +165,38 @@ public class LocationControllerImplTest extends SysuiTestCase {
@Test
public void testCallbackNotified_additionalOps() {
LocationChangeCallback callback = mock(LocationChangeCallback.class);
-
mLocationController.addCallback(callback);
+ mDeviceConfigProxy.setProperty(
+ DeviceConfig.NAMESPACE_PRIVACY,
+ SystemUiDeviceConfigFlags.PROPERTY_LOCATION_INDICATORS_SMALL_ENABLED,
+ "true",
+ true);
+ mTestableLooper.processAllMessages();
+
+ when(mAppOpsController.getActiveAppOps())
+ .thenReturn(ImmutableList.of(
+ new AppOpItem(AppOpsManager.OP_FINE_LOCATION, 0,
+ "com.google.android.googlequicksearchbox",
+ System.currentTimeMillis())));
+ mLocationController.onActiveStateChanged(AppOpsManager.OP_FINE_LOCATION, 0,
+ "com.google.android.googlequicksearchbox", true);
mTestableLooper.processAllMessages();
- mLocationController.onReceive(mContext, new Intent(LocationManager.MODE_CHANGED_ACTION));
+ verify(callback, times(1)).onLocationActiveChanged(true);
+ when(mAppOpsController.getActiveAppOps()).thenReturn(ImmutableList.of());
+ mLocationController.onActiveStateChanged(AppOpsManager.OP_FINE_LOCATION, 0,
+ "com.google.android.googlequicksearchbox", false);
mTestableLooper.processAllMessages();
- verify(callback, times(2)).onLocationSettingsChanged(anyBoolean());
+ verify(callback, times(1)).onLocationActiveChanged(false);
+ }
+ @Test
+ public void testCallbackNotified_additionalOps_shouldShowSystem() {
+ LocationChangeCallback callback = mock(LocationChangeCallback.class);
+ mLocationController.addCallback(callback);
mDeviceConfigProxy.setProperty(
DeviceConfig.NAMESPACE_PRIVACY,
SystemUiDeviceConfigFlags.PROPERTY_LOCATION_INDICATORS_SMALL_ENABLED,
@@ -181,10 +206,40 @@ public class LocationControllerImplTest extends SysuiTestCase {
when(mAppOpsController.getActiveAppOps())
.thenReturn(ImmutableList.of(
- new AppOpItem(AppOpsManager.OP_FINE_LOCATION, 0, "",
+ new AppOpItem(AppOpsManager.OP_FINE_LOCATION, 0,
+ "com.google.android.gms",
System.currentTimeMillis())));
mLocationController.onActiveStateChanged(AppOpsManager.OP_FINE_LOCATION, 0,
- "", true);
+ "com.google.android.gms", true);
+
+ mTestableLooper.processAllMessages();
+
+ verify(callback, times(0)).onLocationActiveChanged(true);
+ }
+
+
+ @Test
+ public void testCallbackNotified_additionalOps_shouldNotShowSystem() {
+ LocationChangeCallback callback = mock(LocationChangeCallback.class);
+ mLocationController.addCallback(callback);
+ mDeviceConfigProxy.setProperty(
+ DeviceConfig.NAMESPACE_PRIVACY,
+ SystemUiDeviceConfigFlags.PROPERTY_LOCATION_INDICATORS_SMALL_ENABLED,
+ "true",
+ true);
+ mDeviceConfigProxy.setProperty(
+ DeviceConfig.NAMESPACE_PRIVACY,
+ SystemUiDeviceConfigFlags.PROPERTY_LOCATION_INDICATORS_SHOW_SYSTEM,
+ "true",
+ true);
+ mTestableLooper.processAllMessages();
+
+ when(mAppOpsController.getActiveAppOps())
+ .thenReturn(ImmutableList.of(
+ new AppOpItem(AppOpsManager.OP_FINE_LOCATION, 0, "com.google.android.gms",
+ System.currentTimeMillis())));
+ mLocationController.onActiveStateChanged(AppOpsManager.OP_FINE_LOCATION, 0,
+ "com.google.android.gms", true);
mTestableLooper.processAllMessages();
@@ -192,7 +247,7 @@ public class LocationControllerImplTest extends SysuiTestCase {
when(mAppOpsController.getActiveAppOps()).thenReturn(ImmutableList.of());
mLocationController.onActiveStateChanged(AppOpsManager.OP_FINE_LOCATION, 0,
- "", false);
+ "com.google.android.gms", false);
mTestableLooper.processAllMessages();
verify(callback, times(1)).onLocationActiveChanged(false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt
index f600b12993b9..4e2736c74007 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt
@@ -68,7 +68,7 @@ class UnfoldLatencyTrackerTest : SysuiTestCase() {
context.mainExecutor,
context,
screenLifecycle
- )
+ ).apply { init() }
deviceStates = FoldableTestUtils.findDeviceStates(context)
verify(deviceStateManager).registerCallback(any(), foldStateListenerCaptor.capture())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardStateController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardStateController.java
index e136d00b86f8..aaea4ecdc08d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardStateController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardStateController.java
@@ -129,11 +129,6 @@ public class FakeKeyguardStateController implements KeyguardStateController {
}
@Override
- public boolean canPerformSmartSpaceTransition() {
- return false;
- }
-
- @Override
public boolean isKeyguardScreenRotationAllowed() {
return false;
}
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 07351cf386ac..9c49e98b9e36 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -18,6 +18,8 @@ package com.android.systemui.wmshell;
import static android.app.Notification.FLAG_BUBBLE;
import static android.app.PendingIntent.FLAG_MUTABLE;
+import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_DELETED;
+import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED;
import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
import static android.service.notification.NotificationListenerService.REASON_CANCEL;
import static android.service.notification.NotificationListenerService.REASON_CANCEL_ALL;
@@ -1285,6 +1287,58 @@ public class BubblesTest extends SysuiTestCase {
assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */);
}
+ @Test
+ public void testNotificationChannelModified_channelUpdated_removesOverflowBubble()
+ throws Exception {
+ // Setup
+ ExpandableNotificationRow row = mNotificationTestHelper.createShortcutBubble("shortcutId");
+ NotificationEntry entry = row.getEntry();
+ entry.getChannel().setConversationId(
+ row.getEntry().getChannel().getParentChannelId(),
+ "shortcutId");
+ mBubbleController.updateBubble(BubblesManager.notifToBubbleEntry(row.getEntry()));
+ assertTrue(mBubbleController.hasBubbles());
+
+ // Overflow it
+ mBubbleData.dismissBubbleWithKey(entry.getKey(),
+ Bubbles.DISMISS_USER_GESTURE);
+ assertThat(mBubbleData.hasOverflowBubbleWithKey(entry.getKey())).isTrue();
+
+ // Test
+ entry.getChannel().setDeleted(true);
+ mBubbleController.onNotificationChannelModified(entry.getSbn().getPackageName(),
+ entry.getSbn().getUser(),
+ entry.getChannel(),
+ NOTIFICATION_CHANNEL_OR_GROUP_UPDATED);
+ assertThat(mBubbleData.hasOverflowBubbleWithKey(entry.getKey())).isFalse();
+ }
+
+ @Test
+ public void testNotificationChannelModified_channelDeleted_removesOverflowBubble()
+ throws Exception {
+ // Setup
+ ExpandableNotificationRow row = mNotificationTestHelper.createShortcutBubble("shortcutId");
+ NotificationEntry entry = row.getEntry();
+ entry.getChannel().setConversationId(
+ row.getEntry().getChannel().getParentChannelId(),
+ "shortcutId");
+ mBubbleController.updateBubble(BubblesManager.notifToBubbleEntry(row.getEntry()));
+ assertTrue(mBubbleController.hasBubbles());
+
+ // Overflow it
+ mBubbleData.dismissBubbleWithKey(entry.getKey(),
+ Bubbles.DISMISS_USER_GESTURE);
+ assertThat(mBubbleData.hasOverflowBubbleWithKey(entry.getKey())).isTrue();
+
+ // Test
+ entry.getChannel().setDeleted(true);
+ mBubbleController.onNotificationChannelModified(entry.getSbn().getPackageName(),
+ entry.getSbn().getUser(),
+ entry.getChannel(),
+ NOTIFICATION_CHANNEL_OR_GROUP_DELETED);
+ assertThat(mBubbleData.hasOverflowBubbleWithKey(entry.getKey())).isFalse();
+ }
+
/** Creates a bubble using the userId and package. */
private Bubble createBubble(int userId, String pkg) {
final UserHandle userHandle = new UserHandle(userId);
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 9eee83ccb6e2..e12a82a1c62b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
@@ -17,6 +17,8 @@
package com.android.systemui.wmshell;
import static android.app.Notification.FLAG_BUBBLE;
+import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_DELETED;
+import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED;
import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED;
@@ -1104,6 +1106,58 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase {
assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */);
}
+ @Test
+ public void testNotificationChannelModified_channelUpdated_removesOverflowBubble()
+ throws Exception {
+ // Setup
+ ExpandableNotificationRow row = mNotificationTestHelper.createShortcutBubble("shortcutId");
+ NotificationEntry entry = row.getEntry();
+ entry.getChannel().setConversationId(
+ row.getEntry().getChannel().getParentChannelId(),
+ "shortcutId");
+ mBubbleController.updateBubble(BubblesManager.notifToBubbleEntry(row.getEntry()));
+ assertTrue(mBubbleController.hasBubbles());
+
+ // Overflow it
+ mBubbleData.dismissBubbleWithKey(entry.getKey(),
+ Bubbles.DISMISS_USER_GESTURE);
+ assertThat(mBubbleData.hasOverflowBubbleWithKey(entry.getKey())).isTrue();
+
+ // Test
+ entry.getChannel().setDeleted(true);
+ mBubbleController.onNotificationChannelModified(entry.getSbn().getPackageName(),
+ entry.getSbn().getUser(),
+ entry.getChannel(),
+ NOTIFICATION_CHANNEL_OR_GROUP_UPDATED);
+ assertThat(mBubbleData.hasOverflowBubbleWithKey(entry.getKey())).isFalse();
+ }
+
+ @Test
+ public void testNotificationChannelModified_channelDeleted_removesOverflowBubble()
+ throws Exception {
+ // Setup
+ ExpandableNotificationRow row = mNotificationTestHelper.createShortcutBubble("shortcutId");
+ NotificationEntry entry = row.getEntry();
+ entry.getChannel().setConversationId(
+ row.getEntry().getChannel().getParentChannelId(),
+ "shortcutId");
+ mBubbleController.updateBubble(BubblesManager.notifToBubbleEntry(row.getEntry()));
+ assertTrue(mBubbleController.hasBubbles());
+
+ // Overflow it
+ mBubbleData.dismissBubbleWithKey(entry.getKey(),
+ Bubbles.DISMISS_USER_GESTURE);
+ assertThat(mBubbleData.hasOverflowBubbleWithKey(entry.getKey())).isTrue();
+
+ // Test
+ entry.getChannel().setDeleted(true);
+ mBubbleController.onNotificationChannelModified(entry.getSbn().getPackageName(),
+ entry.getSbn().getUser(),
+ entry.getChannel(),
+ NOTIFICATION_CHANNEL_OR_GROUP_DELETED);
+ assertThat(mBubbleData.hasOverflowBubbleWithKey(entry.getKey())).isFalse();
+ }
+
/**
* Sets the bubble metadata flags for this entry. These flags are normally set by
* NotificationManagerService when the notification is sent, however, these tests do not
diff --git a/proto/src/camera.proto b/proto/src/camera.proto
index d07a525fdffa..0338b93c8842 100644
--- a/proto/src/camera.proto
+++ b/proto/src/camera.proto
@@ -62,4 +62,7 @@ message CameraStreamProto {
// The frame counts for each histogram bins
// Expected number of fields: 10
repeated int64 histogram_counts = 13;
+
+ // The dynamic range profile of the stream
+ optional int32 dynamic_range_profile = 14;
}
diff --git a/services/Android.bp b/services/Android.bp
index f33c8c0dae15..26760aa3765a 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -179,6 +179,10 @@ cc_library_shared {
name: "libandroid_servers",
defaults: ["libservices.core-libs"],
whole_static_libs: ["libservices.core"],
+ required: [
+ // TODO: remove after NetworkStatsService is moved to the mainline module.
+ "libcom_android_net_module_util_jni",
+ ],
}
platform_compat_config {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 91e909357bc4..5b580d9d829c 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -3497,6 +3497,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
pw.append("hasWindowMagnificationConnection=").append(
String.valueOf(getWindowMagnificationMgr().isConnected()));
pw.println();
+ mMagnificationProcessor.dump(pw, getValidDisplayList());
final int userCount = mUserStates.size();
for (int i = 0; i < userCount; i++) {
mUserStates.valueAt(i).dump(fd, pw, args);
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java
index 8f15d5cf3e3d..77e3ee51125e 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java
@@ -26,6 +26,10 @@ import static android.view.accessibility.MagnificationAnimationCallback.STUB_ANI
import android.accessibilityservice.MagnificationConfig;
import android.annotation.NonNull;
import android.graphics.Region;
+import android.view.Display;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
/**
* Processor class for AccessibilityService connection to control magnification on the specified
@@ -360,4 +364,29 @@ public class MagnificationProcessor {
private void unregister(int displayId) {
mController.getFullScreenMagnificationController().unregister(displayId);
}
+
+ /** Dumps {@link MagnificationConfig} and magnification region of magnifiers on the displays. */
+ public void dump(final PrintWriter pw, ArrayList<Display> displaysList) {
+ for (int i = 0; i < displaysList.size(); i++) {
+ final int displayId = displaysList.get(i).getDisplayId();
+ final MagnificationConfig config = getMagnificationConfig(displayId);
+ pw.println("Magnifier on display#" + displayId);
+ pw.append(" " + config).println();
+ final Region region = new Region();
+ getCurrentMagnificationRegion(displayId, region, true);
+ if (!region.isEmpty()) {
+ pw.append(" Magnification region=").append(region.toString()).println();
+ }
+ pw.append(" IdOfLastServiceToMagnify="
+ + getIdOfLastServiceToMagnify(config.getMode(), displayId)).println();
+ }
+ }
+
+ private int getIdOfLastServiceToMagnify(int mode, int displayId) {
+ return (mode == MAGNIFICATION_MODE_FULLSCREEN)
+ ? mController.getFullScreenMagnificationController()
+ .getIdOfLastServiceToMagnify(displayId)
+ : mController.getWindowMagnificationMgr().getIdOfLastServiceToMagnify(
+ 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 95c7d446d75e..2fbefa4f14c9 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
@@ -319,6 +319,22 @@ public class WindowMagnificationManager implements
}
/**
+ * Get the ID of the last service that changed the magnification config.
+ *
+ * @param displayId The logical display id.
+ * @return The id
+ */
+ public int getIdOfLastServiceToMagnify(int displayId) {
+ synchronized (mLock) {
+ final WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
+ if (magnifier != null) {
+ return magnifier.mIdOfLastServiceToControl;
+ }
+ }
+ return INVALID_SERVICE_ID;
+ }
+
+ /**
* Enable or disable tracking typing focus for the specific magnification window.
*
* The tracking typing focus should be set to enabled with the following conditions:
@@ -466,6 +482,7 @@ public class WindowMagnificationManager implements
boolean previousEnabled;
synchronized (mLock) {
if (mConnectionWrapper == null) {
+ Slog.w(TAG, "enableWindowMagnification failed: connection null");
return false;
}
WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
@@ -508,6 +525,7 @@ public class WindowMagnificationManager implements
synchronized (mLock) {
WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
if (magnifier == null || mConnectionWrapper == null) {
+ Slog.w(TAG, "disableWindowMagnification failed: connection " + mConnectionWrapper);
return false;
}
disabled = magnifier.disableWindowMagnificationInternal(animationCallback);
diff --git a/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java b/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java
index 93fc0e7262aa..2b7b97737e0f 100644
--- a/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java
+++ b/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java
@@ -254,9 +254,14 @@ class AssociationRequestsProcessor {
private AssociationInfo createAssociationAndNotifyApplication(
@NonNull AssociationRequest request, @NonNull String packageName, @UserIdInt int userId,
@Nullable MacAddress macAddress, @NonNull IAssociationRequestCallback callback) {
- final AssociationInfo association = mService.createAssociation(userId, packageName,
- macAddress, request.getDisplayName(), request.getDeviceProfile(),
- request.isSelfManaged());
+ final AssociationInfo association;
+ final long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ association = mService.createAssociation(userId, packageName, macAddress,
+ request.getDisplayName(), request.getDeviceProfile(), request.isSelfManaged());
+ } finally {
+ Binder.restoreCallingIdentity(callingIdentity);
+ }
try {
callback.onAssociationCreated(association);
diff --git a/services/companion/java/com/android/server/companion/AssociationStore.java b/services/companion/java/com/android/server/companion/AssociationStore.java
index 58fc8f7fe5b6..01905bb2297f 100644
--- a/services/companion/java/com/android/server/companion/AssociationStore.java
+++ b/services/companion/java/com/android/server/companion/AssociationStore.java
@@ -49,7 +49,25 @@ public interface AssociationStore {
/** Listener for any changes to {@link AssociationInfo}-s. */
interface OnChangeListener {
default void onAssociationChanged(
- @ChangeType int changeType, AssociationInfo association) {}
+ @ChangeType int changeType, AssociationInfo association) {
+ switch (changeType) {
+ case CHANGE_TYPE_ADDED:
+ onAssociationAdded(association);
+ break;
+
+ case CHANGE_TYPE_REMOVED:
+ onAssociationRemoved(association);
+ break;
+
+ case CHANGE_TYPE_UPDATED_ADDRESS_CHANGED:
+ onAssociationUpdated(association, true);
+ break;
+
+ case CHANGE_TYPE_UPDATED_ADDRESS_UNCHANGED:
+ onAssociationUpdated(association, false);
+ break;
+ }
+ }
default void onAssociationAdded(AssociationInfo association) {}
diff --git a/services/companion/java/com/android/server/companion/AssociationStoreImpl.java b/services/companion/java/com/android/server/companion/AssociationStoreImpl.java
index dbcdd0f877a1..cda554e9d0cf 100644
--- a/services/companion/java/com/android/server/companion/AssociationStoreImpl.java
+++ b/services/companion/java/com/android/server/companion/AssociationStoreImpl.java
@@ -125,8 +125,7 @@ class AssociationStoreImpl implements AssociationStore {
// Update the MacAddress-to-List<Association> map if needed.
final MacAddress updatedAddress = updated.getDeviceMacAddress();
final MacAddress currentAddress = current.getDeviceMacAddress();
- macAddressChanged = Objects.equals(
- current.getDeviceMacAddress(), updated.getDeviceMacAddress());
+ macAddressChanged = Objects.equals(currentAddress, updatedAddress);
if (macAddressChanged) {
if (currentAddress != null) {
mAddressMap.get(currentAddress).remove(id);
@@ -213,11 +212,9 @@ class AssociationStoreImpl implements AssociationStore {
final Set<Integer> ids = mAddressMap.get(address);
if (ids == null) return Collections.emptyList();
- final List<AssociationInfo> associations = new ArrayList<>();
- for (AssociationInfo association : mIdMap.values()) {
- if (address.equals(association.getDeviceMacAddress())) {
- associations.add(association);
- }
+ final List<AssociationInfo> associations = new ArrayList<>(ids.size());
+ for (Integer id : ids) {
+ associations.add(mIdMap.get(id));
}
return Collections.unmodifiableList(associations);
@@ -263,24 +260,6 @@ class AssociationStoreImpl implements AssociationStore {
synchronized (mListeners) {
for (OnChangeListener listener : mListeners) {
listener.onAssociationChanged(changeType, association);
-
- switch (changeType) {
- case CHANGE_TYPE_ADDED:
- listener.onAssociationAdded(association);
- break;
-
- case CHANGE_TYPE_REMOVED:
- listener.onAssociationRemoved(association);
- break;
-
- case CHANGE_TYPE_UPDATED_ADDRESS_CHANGED:
- listener.onAssociationUpdated(association, true);
- break;
-
- case CHANGE_TYPE_UPDATED_ADDRESS_UNCHANGED:
- listener.onAssociationUpdated(association, false);
- break;
- }
}
}
}
diff --git a/services/companion/java/com/android/server/companion/CompanionApplicationController.java b/services/companion/java/com/android/server/companion/CompanionApplicationController.java
new file mode 100644
index 000000000000..be1bc7907cd5
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/CompanionApplicationController.java
@@ -0,0 +1,317 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.companion;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.UserIdInt;
+import android.companion.AssociationInfo;
+import android.companion.CompanionDeviceService;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.Handler;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.infra.PerUser;
+import com.android.internal.util.CollectionUtils;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Manages communication with companion applications via
+ * {@link android.companion.ICompanionDeviceService} interface, including "connecting" (binding) to
+ * the services, maintaining the connection (the binding), and invoking callback methods such as
+ * {@link CompanionDeviceService#onDeviceAppeared(AssociationInfo)} and
+ * {@link CompanionDeviceService#onDeviceDisappeared(AssociationInfo)} in the application process.
+ *
+ * <p>
+ * The following is the list of the APIs provided by {@link CompanionApplicationController} (to be
+ * utilized by {@link CompanionDeviceManagerService}):
+ * <ul>
+ * <li> {@link #bindCompanionApplication(int, String)}
+ * <li> {@link #unbindCompanionApplication(int, String)}
+ * <li> {@link #notifyCompanionApplicationDeviceAppeared(AssociationInfo)}
+ * <li> {@link #notifyCompanionApplicationDeviceDisappeared(AssociationInfo)}
+ * <li> {@link #isCompanionApplicationBound(int, String)}
+ * <li> {@link #isRebindingCompanionApplicationScheduled(int, String)}
+ * </ul>
+ *
+ * @see CompanionDeviceService
+ * @see android.companion.ICompanionDeviceService
+ * @see CompanionDeviceServiceConnector
+ */
+@SuppressLint("LongLogTag")
+class CompanionApplicationController {
+ static final boolean DEBUG = false;
+ private static final String TAG = "CompanionDevice_ApplicationController";
+
+ private static final long REBIND_TIMEOUT = 10 * 1000; // 10 sec
+
+ interface Callback {
+ /**
+ * @return {@code true} if should schedule rebinding.
+ * {@code false} if we do not need to rebind.
+ */
+ boolean onCompanionApplicationBindingDied(
+ @UserIdInt int userId, @NonNull String packageName);
+
+ /**
+ * Callback after timeout for previously scheduled rebind has passed.
+ */
+ void onRebindCompanionApplicationTimeout(
+ @UserIdInt int userId, @NonNull String packageName);
+ }
+
+ private final @NonNull Context mContext;
+ private final @NonNull Callback mCallback;
+ private final @NonNull CompanionServicesRegister mCompanionServicesRegister;
+ @GuardedBy("mBoundCompanionApplications")
+ private final @NonNull AndroidPackageMap<List<CompanionDeviceServiceConnector>>
+ mBoundCompanionApplications;
+ private final @NonNull AndroidPackageMap<Boolean> mScheduledForRebindingCompanionApplications;
+
+ CompanionApplicationController(Context context, Callback callback) {
+ mContext = context;
+ mCallback = callback;
+ mCompanionServicesRegister = new CompanionServicesRegister();
+ mBoundCompanionApplications = new AndroidPackageMap<>();
+ mScheduledForRebindingCompanionApplications = new AndroidPackageMap<>();
+ }
+
+ void onPackagesChanged(@UserIdInt int userId) {
+ mCompanionServicesRegister.invalidate(userId);
+ }
+
+ void bindCompanionApplication(@UserIdInt int userId, @NonNull String packageName) {
+ if (DEBUG) Log.i(TAG, "bind() u" + userId + "/" + packageName);
+
+ final List<ComponentName> companionServices =
+ mCompanionServicesRegister.forPackage(userId, packageName);
+ final List<CompanionDeviceServiceConnector> serviceConnectors;
+
+ synchronized (mBoundCompanionApplications) {
+ if (mBoundCompanionApplications.containsValueForPackage(userId, packageName)) {
+ if (DEBUG) Log.e(TAG, "u" + userId + "/" + packageName + " is ALREADY bound.");
+ return;
+ }
+
+ serviceConnectors = CollectionUtils.map(companionServices, componentName ->
+ new CompanionDeviceServiceConnector(mContext, userId, componentName));
+ mBoundCompanionApplications.setValueForPackage(userId, packageName, serviceConnectors);
+ }
+
+ // The first connector in the list is always the primary connector: set a listener to it.
+ serviceConnectors.get(0).setListener(this::onPrimaryServiceBindingDied);
+
+ // Now "bind" all the connectors: the primary one and the rest of them.
+ for (CompanionDeviceServiceConnector serviceConnector : serviceConnectors) {
+ serviceConnector.connect();
+ }
+ }
+
+ void unbindCompanionApplication(@UserIdInt int userId, @NonNull String packageName) {
+ if (DEBUG) Log.i(TAG, "unbind() u" + userId + "/" + packageName);
+
+ final List<CompanionDeviceServiceConnector> serviceConnectors;
+ synchronized (mBoundCompanionApplications) {
+ serviceConnectors = mBoundCompanionApplications.removePackage(userId, packageName);
+ }
+ if (serviceConnectors == null) {
+ if (DEBUG) Log.e(TAG, "u" + userId + "/" + packageName + " is NOT bound");
+ return;
+ }
+
+ for (CompanionDeviceServiceConnector serviceConnector : serviceConnectors) {
+ serviceConnector.postUnbind();
+ }
+ }
+
+ boolean isCompanionApplicationBound(@UserIdInt int userId, @NonNull String packageName) {
+ synchronized (mBoundCompanionApplications) {
+ return mBoundCompanionApplications.containsValueForPackage(userId, packageName);
+ }
+ }
+
+ private void scheduleRebinding(@UserIdInt int userId, @NonNull String packageName) {
+ mScheduledForRebindingCompanionApplications.setValueForPackage(userId, packageName, true);
+
+ Handler.getMain().postDelayed(() ->
+ onRebindingCompanionApplicationTimeout(userId, packageName), REBIND_TIMEOUT);
+ }
+
+ boolean isRebindingCompanionApplicationScheduled(
+ @UserIdInt int userId, @NonNull String packageName) {
+ return mScheduledForRebindingCompanionApplications
+ .containsValueForPackage(userId, packageName);
+ }
+
+ private void onRebindingCompanionApplicationTimeout(
+ @UserIdInt int userId, @NonNull String packageName) {
+ mScheduledForRebindingCompanionApplications.removePackage(userId, packageName);
+
+ mCallback.onRebindCompanionApplicationTimeout(userId, packageName);
+ }
+
+ void notifyCompanionApplicationDeviceAppeared(AssociationInfo association) {
+ final int userId = association.getUserId();
+ final String packageName = association.getPackageName();
+ if (DEBUG) {
+ Log.i(TAG, "notifyDevice_Appeared() id=" + association.getId() + " u" + userId
+ + "/" + packageName);
+ }
+
+ final CompanionDeviceServiceConnector primaryServiceConnector =
+ getPrimaryServiceConnector(userId, packageName);
+ if (primaryServiceConnector == null) {
+ if (DEBUG) Log.e(TAG, "u" + userId + "/" + packageName + " is NOT bound.");
+ return;
+ }
+
+ primaryServiceConnector.postOnDeviceAppeared(association);
+ }
+
+ void notifyCompanionApplicationDeviceDisappeared(AssociationInfo association) {
+ final int userId = association.getUserId();
+ final String packageName = association.getPackageName();
+ if (DEBUG) {
+ Log.i(TAG, "notifyDevice_Disappeared() id=" + association.getId() + " u" + userId
+ + "/" + packageName);
+ }
+
+ final CompanionDeviceServiceConnector primaryServiceConnector =
+ getPrimaryServiceConnector(userId, packageName);
+ if (primaryServiceConnector == null) {
+ if (DEBUG) Log.e(TAG, "u" + userId + "/" + packageName + " is NOT bound.");
+ return;
+ }
+
+ primaryServiceConnector.postOnDeviceDisappeared(association);
+ }
+
+ private void onPrimaryServiceBindingDied(@UserIdInt int userId, @NonNull String packageName) {
+ if (DEBUG) Log.i(TAG, "onPrimaryServiceBindingDied() u" + userId + "/" + packageName);
+
+ // First: mark as NOT bound.
+ synchronized (mBoundCompanionApplications) {
+ mBoundCompanionApplications.removePackage(userId, packageName);
+ }
+
+ // Second: invoke callback, schedule rebinding if needed.
+ final boolean shouldScheduleRebind =
+ mCallback.onCompanionApplicationBindingDied(userId, packageName);
+ if (shouldScheduleRebind) {
+ scheduleRebinding(userId, packageName);
+ }
+ }
+
+ private @Nullable CompanionDeviceServiceConnector getPrimaryServiceConnector(
+ @UserIdInt int userId, @NonNull String packageName) {
+ final List<CompanionDeviceServiceConnector> connectors;
+ synchronized (mBoundCompanionApplications) {
+ connectors = mBoundCompanionApplications.getValueForPackage(userId, packageName);
+ }
+ return connectors != null ? connectors.get(0) : null;
+ }
+
+ private class CompanionServicesRegister extends PerUser<Map<String, List<ComponentName>>> {
+ @Override
+ public synchronized @NonNull Map<String, List<ComponentName>> forUser(
+ @UserIdInt int userId) {
+ return super.forUser(userId);
+ }
+
+ synchronized @NonNull List<ComponentName> forPackage(
+ @UserIdInt int userId, @NonNull String packageName) {
+ return forUser(userId).getOrDefault(packageName, Collections.emptyList());
+ }
+
+ synchronized @NonNull ComponentName primaryForPackage(
+ @UserIdInt int userId, @NonNull String packageName) {
+ // The primary service is always at the head of the list.
+ return forPackage(userId, packageName).get(0);
+ }
+
+ synchronized void invalidate(@UserIdInt int userId) {
+ remove(userId);
+ }
+
+ @Override
+ protected final @NonNull Map<String, List<ComponentName>> create(@UserIdInt int userId) {
+ return PackageUtils.getCompanionServicesForUser(mContext, userId);
+ }
+ }
+
+ /**
+ * Associates an Android package (defined by userId + packageName) with a value of type T.
+ */
+ private static class AndroidPackageMap<T> extends SparseArray<Map<String, T>> {
+
+ void setValueForPackage(
+ @UserIdInt int userId, @NonNull String packageName, @NonNull T value) {
+ Map<String, T> forUser = get(userId);
+ if (forUser == null) {
+ forUser = /* Map<String, T> */ new HashMap();
+ put(userId, forUser);
+ }
+
+ forUser.put(packageName, value);
+ }
+
+ boolean containsValueForPackage(@UserIdInt int userId, @NonNull String packageName) {
+ final Map<String, ?> forUser = get(userId);
+ return forUser != null && forUser.containsKey(packageName);
+ }
+
+ T getValueForPackage(@UserIdInt int userId, @NonNull String packageName) {
+ final Map<String, T> forUser = get(userId);
+ return forUser != null ? forUser.get(packageName) : null;
+ }
+
+ T removePackage(@UserIdInt int userId, @NonNull String packageName) {
+ final Map<String, T> forUser = get(userId);
+ if (forUser == null) return null;
+ return forUser.remove(packageName);
+ }
+
+ void dump() {
+ if (size() == 0) {
+ Log.d(TAG, "<empty>");
+ return;
+ }
+
+ for (int i = 0; i < size(); i++) {
+ final int userId = keyAt(i);
+ final Map<String, T> forUser = get(userId);
+ if (forUser.isEmpty()) {
+ Log.d(TAG, "u" + userId + ": <empty>");
+ }
+
+ for (Map.Entry<String, T> packageValue : forUser.entrySet()) {
+ final String packageName = packageValue.getKey();
+ final T value = packageValue.getValue();
+ Log.d(TAG, "u" + userId + "\\" + packageName + " -> " + value);
+ }
+ }
+ }
+ }
+}
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 1e50c80f0e71..cfd37988d234 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -77,11 +77,13 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
+import android.content.pm.UserInfo;
import android.net.MacAddress;
import android.net.NetworkPolicyManager;
import android.os.Binder;
import android.os.Environment;
import android.os.Handler;
+import android.os.Message;
import android.os.Parcel;
import android.os.PowerWhitelistManager;
import android.os.RemoteCallbackList;
@@ -185,6 +187,7 @@ public class CompanionDeviceManagerService extends SystemService
private final CompanionDeviceManagerServiceInternal mLocalService = new LocalService(this);
final Handler mMainHandler = Handler.getMain();
+ private final PersistUserStateHandler mUserPersistenceHandler = new PersistUserStateHandler();
private CompanionDevicePresenceController mCompanionDevicePresenceController;
/**
@@ -366,9 +369,8 @@ public class CompanionDeviceManagerService extends SystemService
final List<AssociationInfo> updatedAssociations =
mAssociationStore.getAssociationsForUser(userId);
- final Map<String, Set<Integer>> usedIdsForUser = getPreviouslyUsedIdsForUser(userId);
- BackgroundThread.getHandler().post(() ->
- mPersistentStore.persistStateForUser(userId, updatedAssociations, usedIdsForUser));
+
+ mUserPersistenceHandler.postPersistUserState(userId);
// Notify listeners if ADDED, REMOVED or UPDATED_ADDRESS_CHANGED.
// Do NOT notify when UPDATED_ADDRESS_UNCHANGED, which means a minor tweak in association's
@@ -381,6 +383,13 @@ public class CompanionDeviceManagerService extends SystemService
restartBleScan();
}
+ private void persistStateForUser(@UserIdInt int userId) {
+ final List<AssociationInfo> updatedAssociations =
+ mAssociationStore.getAssociationsForUser(userId);
+ final Map<String, Set<Integer>> usedIdsForUser = getPreviouslyUsedIdsForUser(userId);
+ mPersistentStore.persistStateForUser(userId, updatedAssociations, usedIdsForUser);
+ }
+
private void notifyListeners(
@UserIdInt int userId, @NonNull List<AssociationInfo> associations) {
mListeners.broadcast((listener, callbackUserId) -> {
@@ -778,6 +787,12 @@ public class CompanionDeviceManagerService extends SystemService
Slog.i(LOG_TAG, "New CDM association created=" + association);
mAssociationStore.addAssociation(association);
+ // If the "Device Profile" is specified, make the companion application a holder of the
+ // corresponding role.
+ if (deviceProfile != null) {
+ addRoleHolderForAssociation(getContext(), association);
+ }
+
updateSpecialAccessPermissionForAssociatedPackage(association);
return association;
@@ -921,14 +936,8 @@ public class CompanionDeviceManagerService extends SystemService
exemptFromAutoRevoke(packageInfo.packageName, packageInfo.applicationInfo.uid);
- if (!association.isSelfManaged()) {
- if (mCurrentlyConnectedDevices.contains(association.getDeviceMacAddressAsString())) {
- addRoleHolderForAssociation(getContext(), association);
- }
-
- if (association.isNotifyOnDeviceNearby()) {
- restartBleScan();
- }
+ if (association.isNotifyOnDeviceNearby()) {
+ restartBleScan();
}
}
@@ -974,19 +983,7 @@ public class CompanionDeviceManagerService extends SystemService
void onDeviceConnected(String address) {
Slog.d(LOG_TAG, "onDeviceConnected(address = " + address + ")");
-
mCurrentlyConnectedDevices.add(address);
-
- for (AssociationInfo association : mAssociationStore.getAssociationsByAddress(address)) {
- if (association.getDeviceProfile() != null) {
- Slog.i(LOG_TAG, "Granting role " + association.getDeviceProfile()
- + " to " + association.getPackageName()
- + " due to device connected: " + association.getDeviceMacAddress());
-
- addRoleHolderForAssociation(getContext(), association);
- }
- }
-
onDeviceNearby(address);
}
@@ -1349,4 +1346,47 @@ public class CompanionDeviceManagerService extends SystemService
mService.associationCleanUp(profile);
}
}
+
+ /**
+ * This method must only be called from {@link CompanionDeviceShellCommand} for testing
+ * purposes only!
+ */
+ void persistState() {
+ mUserPersistenceHandler.clearMessages();
+ for (UserInfo user : mUserManager.getAliveUsers()) {
+ persistStateForUser(user.id);
+ }
+ }
+
+ /**
+ * This class is dedicated to handling requests to persist user state.
+ */
+ @SuppressLint("HandlerLeak")
+ private class PersistUserStateHandler extends Handler {
+ PersistUserStateHandler() {
+ super(BackgroundThread.get().getLooper());
+ }
+
+ /**
+ * Persists user state unless there is already an outstanding request for the given user.
+ */
+ synchronized void postPersistUserState(@UserIdInt int userId) {
+ if (!hasMessages(userId)) {
+ sendMessage(obtainMessage(userId));
+ }
+ }
+
+ /**
+ * Clears *ALL* outstanding persist requests for *ALL* users.
+ */
+ synchronized void clearMessages() {
+ removeCallbacksAndMessages(null);
+ }
+
+ @Override
+ public void handleMessage(@NonNull Message msg) {
+ final int userId = msg.what;
+ persistStateForUser(userId);
+ }
+ }
}
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
index 5f46d5c4c4bf..f2e660779e9e 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
@@ -73,19 +73,12 @@ class CompanionDeviceShellCommand extends android.os.ShellCommand {
}
break;
- case "simulate_connect": {
- mService.onDeviceConnected(getNextArgRequired());
- }
- break;
-
- case "simulate_disconnect": {
- mService.onDeviceDisconnected(getNextArgRequired());
- }
- break;
case "clear-association-memory-cache": {
+ mService.persistState();
mService.loadAssociationsFromDisk();
}
break;
+
default:
return handleDefaultCommands(cmd);
}
diff --git a/services/companion/java/com/android/server/companion/DataStoreUtils.java b/services/companion/java/com/android/server/companion/DataStoreUtils.java
new file mode 100644
index 000000000000..6055a81c7ca7
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/DataStoreUtils.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.companion;
+
+import static org.xmlpull.v1.XmlPullParser.END_TAG;
+import static org.xmlpull.v1.XmlPullParser.START_TAG;
+
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.os.Environment;
+import android.util.AtomicFile;
+import android.util.Slog;
+
+import com.android.internal.util.FunctionalUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.File;
+import java.io.FileOutputStream;
+
+final class DataStoreUtils {
+
+ private static final String LOG_TAG = DataStoreUtils.class.getSimpleName();
+
+ static boolean isStartOfTag(@NonNull XmlPullParser parser, @NonNull String tag)
+ throws XmlPullParserException {
+ return parser.getEventType() == START_TAG && tag.equals(parser.getName());
+ }
+
+ static boolean isEndOfTag(@NonNull XmlPullParser parser, @NonNull String tag)
+ throws XmlPullParserException {
+ return parser.getEventType() == END_TAG && tag.equals(parser.getName());
+ }
+
+ /**
+ * Creates {@link AtomicFile} object that represents the back-up for the given user.
+ *
+ * IMPORTANT: the method will ALWAYS return the same {@link AtomicFile} object, which makes it
+ * possible to synchronize reads and writes to the file using the returned object.
+ *
+ * @param userId the userId to retrieve the storage file
+ * @param fileName the storage file name
+ * @return an AtomicFile for the user
+ */
+ @NonNull
+ static AtomicFile createStorageFileForUser(@UserIdInt int userId, String fileName) {
+ return new AtomicFile(getBaseStorageFileForUser(userId, fileName));
+ }
+
+ @NonNull
+ private static File getBaseStorageFileForUser(@UserIdInt int userId, String fileName) {
+ return new File(Environment.getDataSystemDeDirectory(userId), fileName);
+ }
+
+ /**
+ * Writing to file could fail, for example, if the user has been recently removed and so was
+ * their DE (/data/system_de/<user-id>/) directory.
+ */
+ static void writeToFileSafely(@NonNull AtomicFile file,
+ @NonNull FunctionalUtils.ThrowingConsumer<FileOutputStream> consumer) {
+ try {
+ file.write(consumer);
+ } catch (Exception e) {
+ Slog.e(LOG_TAG, "Error while writing to file " + file, e);
+ }
+ }
+
+ private DataStoreUtils() {
+ }
+}
diff --git a/services/companion/java/com/android/server/companion/PersistentDataStore.java b/services/companion/java/com/android/server/companion/PersistentDataStore.java
index 3c8c3cb8f842..da33b4446840 100644
--- a/services/companion/java/com/android/server/companion/PersistentDataStore.java
+++ b/services/companion/java/com/android/server/companion/PersistentDataStore.java
@@ -25,9 +25,10 @@ import static com.android.internal.util.XmlUtils.writeBooleanAttribute;
import static com.android.internal.util.XmlUtils.writeIntAttribute;
import static com.android.internal.util.XmlUtils.writeLongAttribute;
import static com.android.internal.util.XmlUtils.writeStringAttribute;
-
-import static org.xmlpull.v1.XmlPullParser.END_TAG;
-import static org.xmlpull.v1.XmlPullParser.START_TAG;
+import static com.android.server.companion.DataStoreUtils.createStorageFileForUser;
+import static com.android.server.companion.DataStoreUtils.isEndOfTag;
+import static com.android.server.companion.DataStoreUtils.isStartOfTag;
+import static com.android.server.companion.DataStoreUtils.writeToFileSafely;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -44,7 +45,6 @@ import android.util.TypedXmlPullParser;
import android.util.TypedXmlSerializer;
import android.util.Xml;
-import com.android.internal.util.FunctionalUtils.ThrowingConsumer;
import com.android.internal.util.XmlUtils;
import org.xmlpull.v1.XmlPullParser;
@@ -53,7 +53,6 @@ import org.xmlpull.v1.XmlSerializer;
import java.io.File;
import java.io.FileInputStream;
-import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Collection;
import java.util.HashSet;
@@ -210,6 +209,7 @@ final class PersistentDataStore {
@NonNull Collection<AssociationInfo> associationsOut,
@NonNull Map<String, Set<Integer>> previouslyUsedIdsPerPackageOut) {
Slog.i(LOG_TAG, "Reading associations for user " + userId + " from disk");
+
final AtomicFile file = getStorageFileForUser(userId);
if (DEBUG) Slog.d(LOG_TAG, " > File=" + file.getBaseFile().getPath());
@@ -349,11 +349,7 @@ final class PersistentDataStore {
*/
private @NonNull AtomicFile getStorageFileForUser(@UserIdInt int userId) {
return mUserIdToStorageFile.computeIfAbsent(userId,
- u -> new AtomicFile(getBaseStorageFileForUser(userId)));
- }
-
- private static @NonNull File getBaseStorageFileForUser(@UserIdInt int userId) {
- return new File(Environment.getDataSystemDeDirectory(userId), FILE_NAME);
+ u -> createStorageFileForUser(userId, FILE_NAME));
}
private static @NonNull File getBaseLegacyStorageFileForUser(@UserIdInt int userId) {
@@ -512,16 +508,6 @@ final class PersistentDataStore {
serializer.endTag(null, XML_TAG_PACKAGE);
}
- private static boolean isStartOfTag(@NonNull XmlPullParser parser, @NonNull String tag)
- throws XmlPullParserException {
- return parser.getEventType() == START_TAG && tag.equals(parser.getName());
- }
-
- private static boolean isEndOfTag(@NonNull XmlPullParser parser, @NonNull String tag)
- throws XmlPullParserException {
- return parser.getEventType() == END_TAG && tag.equals(parser.getName());
- }
-
private static void requireStartOfTag(@NonNull XmlPullParser parser, @NonNull String tag)
throws XmlPullParserException {
if (isStartOfTag(parser, tag)) return;
@@ -546,13 +532,4 @@ final class PersistentDataStore {
}
return associationInfo;
}
-
- private static void writeToFileSafely(@NonNull AtomicFile file,
- @NonNull ThrowingConsumer<FileOutputStream> consumer) {
- try {
- file.write(consumer);
- } catch (Exception e) {
- Slog.e(LOG_TAG, "Error while writing to file " + file, e);
- }
- }
}
diff --git a/services/companion/java/com/android/server/companion/SystemDataTransferRequestDataStore.java b/services/companion/java/com/android/server/companion/SystemDataTransferRequestDataStore.java
new file mode 100644
index 000000000000..38e5d16842b9
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/SystemDataTransferRequestDataStore.java
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.companion;
+
+import static com.android.internal.util.XmlUtils.readBooleanAttribute;
+import static com.android.internal.util.XmlUtils.readIntAttribute;
+import static com.android.internal.util.XmlUtils.readThisListXml;
+import static com.android.internal.util.XmlUtils.writeBooleanAttribute;
+import static com.android.internal.util.XmlUtils.writeIntAttribute;
+import static com.android.internal.util.XmlUtils.writeListXml;
+import static com.android.server.companion.DataStoreUtils.createStorageFileForUser;
+import static com.android.server.companion.DataStoreUtils.isEndOfTag;
+import static com.android.server.companion.DataStoreUtils.isStartOfTag;
+import static com.android.server.companion.DataStoreUtils.writeToFileSafely;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.companion.SystemDataTransferRequest;
+import android.util.AtomicFile;
+import android.util.Slog;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
+import android.util.Xml;
+
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * The class is responsible for reading/writing SystemDataTransferRequest records from/to the disk.
+ *
+ * The following snippet is a sample XML file stored in the disk.
+ * <pre>{@code
+ * <requests>
+ * <request
+ * association_id="1"
+ * is_permission_sync_all_packages="false">
+ * <list name="permission_sync_packages">
+ * <string>com.sample.app1</string>
+ * <string>com.sample.app2</string>
+ * </list>
+ * </request>
+ * </requests>
+ * }</pre>
+ */
+public class SystemDataTransferRequestDataStore {
+
+ private static final String LOG_TAG = SystemDataTransferRequestDataStore.class.getSimpleName();
+
+ private static final String FILE_NAME = "companion_device_system_data_transfer_requests.xml";
+
+ private static final String XML_TAG_REQUESTS = "requests";
+ private static final String XML_TAG_REQUEST = "request";
+ private static final String XML_TAG_LIST = "list";
+
+ private static final String XML_ATTR_ASSOCIATION_ID = "association_id";
+ private static final String XML_ATTR_IS_PERMISSION_SYNC_ALL_PACKAGES =
+ "is_permission_sync_all_packages";
+ private static final String XML_ATTR_PERMISSION_SYNC_PACKAGES = "permission_sync_packages";
+
+ private final ConcurrentMap<Integer, AtomicFile> mUserIdToStorageFile =
+ new ConcurrentHashMap<>();
+
+ /**
+ * Reads previously persisted data for the given user
+ *
+ * @param userId Android UserID
+ * @return a list of SystemDataTransferRequest
+ */
+ @NonNull
+ List<SystemDataTransferRequest> readRequestsForUser(@UserIdInt int userId) {
+ final AtomicFile file = getStorageFileForUser(userId);
+ Slog.i(LOG_TAG, "Reading SystemDataTransferRequests for user " + userId + " from "
+ + "file=" + file.getBaseFile().getPath());
+
+ // getStorageFileForUser() ALWAYS returns the SAME OBJECT, which allows us to synchronize
+ // accesses to the file on the file system using this AtomicFile object.
+ synchronized (file) {
+ if (!file.getBaseFile().exists()) {
+ Slog.d(LOG_TAG, "File does not exist -> Abort");
+ return Collections.emptyList();
+ }
+ try (FileInputStream in = file.openRead()) {
+ final TypedXmlPullParser parser = Xml.resolvePullParser(in);
+ XmlUtils.beginDocument(parser, XML_TAG_REQUESTS);
+
+ return readRequests(parser);
+ } catch (XmlPullParserException | IOException e) {
+ Slog.e(LOG_TAG, "Error while reading requests file", e);
+ return Collections.emptyList();
+ }
+ }
+ }
+
+ @NonNull
+ private List<SystemDataTransferRequest> readRequests(@NonNull TypedXmlPullParser parser)
+ throws XmlPullParserException, IOException {
+ if (!isStartOfTag(parser, XML_TAG_REQUESTS)) {
+ throw new XmlPullParserException("The XML doesn't have start tag: " + XML_TAG_REQUESTS);
+ }
+
+ List<SystemDataTransferRequest> requests = new ArrayList<>();
+
+ while (true) {
+ parser.nextTag();
+ if (isEndOfTag(parser, XML_TAG_REQUESTS)) break;
+ if (isStartOfTag(parser, XML_TAG_REQUEST)) {
+ requests.add(readRequest(parser));
+ }
+ }
+
+ return requests;
+ }
+
+ private SystemDataTransferRequest readRequest(@NonNull TypedXmlPullParser parser)
+ throws XmlPullParserException, IOException {
+ if (!isStartOfTag(parser, XML_TAG_REQUEST)) {
+ throw new XmlPullParserException("XML doesn't have start tag: " + XML_TAG_REQUEST);
+ }
+
+ final int associationId = readIntAttribute(parser, XML_ATTR_ASSOCIATION_ID);
+ final boolean isPermissionSyncAllPackages = readBooleanAttribute(parser,
+ XML_ATTR_IS_PERMISSION_SYNC_ALL_PACKAGES);
+ parser.nextTag();
+ List<String> permissionSyncPackages = new ArrayList<>();
+ if (isStartOfTag(parser, XML_TAG_LIST)) {
+ parser.nextTag();
+ permissionSyncPackages = readThisListXml(parser, XML_TAG_LIST,
+ new String[1]);
+ }
+
+ return new SystemDataTransferRequest(associationId, isPermissionSyncAllPackages,
+ permissionSyncPackages);
+ }
+
+ /**
+ * Persisted user's SystemDataTransferRequest data to the disk.
+ *
+ * @param userId Android UserID
+ * @param requests a list of user's SystemDataTransferRequest.
+ */
+ void writeRequestsForUser(@UserIdInt int userId,
+ @NonNull List<SystemDataTransferRequest> requests) {
+ final AtomicFile file = getStorageFileForUser(userId);
+ Slog.i(LOG_TAG, "Writing SystemDataTransferRequests for user " + userId + " to file="
+ + file.getBaseFile().getPath());
+
+ // getStorageFileForUser() ALWAYS returns the SAME OBJECT, which allows us to synchronize
+ // accesses to the file on the file system using this AtomicFile object.
+ synchronized (file) {
+ writeToFileSafely(file, out -> {
+ final TypedXmlSerializer serializer = Xml.resolveSerializer(out);
+ serializer.setFeature(
+ "http://xmlpull.org/v1/doc/features.html#indent-output", true);
+ serializer.startDocument(null, true);
+ writeRequests(serializer, requests);
+ serializer.endDocument();
+ });
+ }
+ }
+
+ private void writeRequests(@NonNull TypedXmlSerializer serializer,
+ @Nullable Collection<SystemDataTransferRequest> requests) throws IOException {
+ serializer.startTag(null, XML_TAG_REQUESTS);
+
+ for (SystemDataTransferRequest request : requests) {
+ writeRequest(serializer, request);
+ }
+
+ serializer.endTag(null, XML_TAG_REQUESTS);
+ }
+
+ private void writeRequest(@NonNull TypedXmlSerializer serializer,
+ @NonNull SystemDataTransferRequest request) throws IOException {
+ serializer.startTag(null, XML_TAG_REQUEST);
+
+ writeIntAttribute(serializer, XML_ATTR_ASSOCIATION_ID, request.getAssociationId());
+ writeBooleanAttribute(serializer, XML_ATTR_IS_PERMISSION_SYNC_ALL_PACKAGES,
+ request.isPermissionSyncAllPackages());
+ try {
+ writeListXml(request.getPermissionSyncPackages(), XML_ATTR_PERMISSION_SYNC_PACKAGES,
+ serializer);
+ } catch (XmlPullParserException e) {
+ Slog.e(LOG_TAG, "Error writing permission sync packages into XML. "
+ + request.getPermissionSyncPackages().toString());
+ }
+
+ serializer.endTag(null, XML_TAG_REQUEST);
+ }
+
+ /**
+ * Creates and caches {@link AtomicFile} object that represents the back-up file for the given
+ * user.
+ *
+ * IMPORTANT: the method will ALWAYS return the same {@link AtomicFile} object, which makes it
+ * possible to synchronize reads and writes to the file using the returned object.
+ */
+ private @NonNull AtomicFile getStorageFileForUser(@UserIdInt int userId) {
+ return mUserIdToStorageFile.computeIfAbsent(userId,
+ u -> createStorageFileForUser(userId, FILE_NAME));
+ }
+}
diff --git a/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java b/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java
new file mode 100644
index 000000000000..0eb6b8d24768
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java
@@ -0,0 +1,436 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.android.server.companion.presence;
+
+import static android.bluetooth.BluetoothAdapter.ACTION_BLE_STATE_CHANGED;
+import static android.bluetooth.BluetoothAdapter.ACTION_STATE_CHANGED;
+import static android.bluetooth.BluetoothAdapter.EXTRA_PREVIOUS_STATE;
+import static android.bluetooth.BluetoothAdapter.EXTRA_STATE;
+import static android.bluetooth.BluetoothAdapter.STATE_BLE_ON;
+import static android.bluetooth.BluetoothAdapter.STATE_ON;
+import static android.bluetooth.BluetoothAdapter.nameForState;
+import static android.bluetooth.le.ScanCallback.SCAN_FAILED_ALREADY_STARTED;
+import static android.bluetooth.le.ScanCallback.SCAN_FAILED_APPLICATION_REGISTRATION_FAILED;
+import static android.bluetooth.le.ScanCallback.SCAN_FAILED_FEATURE_UNSUPPORTED;
+import static android.bluetooth.le.ScanCallback.SCAN_FAILED_INTERNAL_ERROR;
+import static android.bluetooth.le.ScanCallback.SCAN_FAILED_OUT_OF_HARDWARE_RESOURCES;
+import static android.bluetooth.le.ScanCallback.SCAN_FAILED_SCANNING_TOO_FREQUENTLY;
+import static android.bluetooth.le.ScanSettings.CALLBACK_TYPE_ALL_MATCHES;
+import static android.bluetooth.le.ScanSettings.CALLBACK_TYPE_FIRST_MATCH;
+import static android.bluetooth.le.ScanSettings.CALLBACK_TYPE_MATCH_LOST;
+import static android.bluetooth.le.ScanSettings.SCAN_MODE_LOW_POWER;
+
+import static com.android.server.companion.presence.Utils.btDeviceToString;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.MainThread;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.le.BluetoothLeScanner;
+import android.bluetooth.le.ScanCallback;
+import android.bluetooth.le.ScanFilter;
+import android.bluetooth.le.ScanResult;
+import android.bluetooth.le.ScanSettings;
+import android.companion.AssociationInfo;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.server.companion.AssociationStore;
+import com.android.server.companion.AssociationStore.ChangeType;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+@SuppressLint("LongLogTag")
+class BleCompanionDeviceScanner implements AssociationStore.OnChangeListener {
+ private static final boolean DEBUG = false;
+ private static final String TAG = "CompanionDevice_PresenceMonitor_BLE";
+
+ /**
+ * When using {@link ScanSettings#SCAN_MODE_LOW_POWER}, it usually takes from 20 seconds to
+ * 2 minutes for the BLE scanner to find advertisements sent from the same device.
+ * On the other hand, {@link android.bluetooth.BluetoothAdapter.LeScanCallback} will report
+ * {@link ScanSettings#CALLBACK_TYPE_MATCH_LOST MATCH_LOST} 10 sec after it finds the
+ * advertisement for the first time (add reports
+ * {@link ScanSettings#CALLBACK_TYPE_FIRST_MATCH FIRST_MATCH}).
+ * To avoid constantly reporting {@link Callback#onBleCompanionDeviceFound(int) onDeviceFound()}
+ * and {@link Callback#onBleCompanionDeviceLost(int) onDeviceLost()} (while the device is
+ * actually present) to its clients, {@link BleCompanionDeviceScanner}, will add 1-minute delay
+ * after it receives {@link ScanSettings#CALLBACK_TYPE_MATCH_LOST MATCH_LOST}.
+ */
+ private static final int NOTIFY_DEVICE_LOST_DELAY = 2 * 60 * 1000; // 2 Min.
+
+ interface Callback {
+ void onBleCompanionDeviceFound(int associationId);
+
+ void onBleCompanionDeviceLost(int associationId);
+ }
+
+ private final @NonNull AssociationStore mAssociationStore;
+ private final @NonNull Callback mCallback;
+ private final @NonNull MainThreadHandler mMainThreadHandler;
+
+ // Non-null after init().
+ private @Nullable BluetoothAdapter mBtAdapter;
+ // Non-null after init() and when BLE is available. Otherwise - null.
+ private @Nullable BluetoothLeScanner mBleScanner;
+ // Only accessed from the Main thread.
+ private boolean mScanning = false;
+
+ BleCompanionDeviceScanner(
+ @NonNull AssociationStore associationStore, @NonNull Callback callback) {
+ mAssociationStore = associationStore;
+ mCallback = callback;
+ mMainThreadHandler = new MainThreadHandler();
+ }
+
+ @MainThread
+ void init(@NonNull Context context, @NonNull BluetoothAdapter btAdapter) {
+ if (DEBUG) Log.i(TAG, "init()");
+
+ if (mBtAdapter != null) {
+ throw new IllegalStateException(getClass().getSimpleName() + " is already initialized");
+ }
+ mBtAdapter = requireNonNull(btAdapter);
+
+ checkBleState();
+ registerBluetoothStateBroadcastReceiver(context);
+
+ mAssociationStore.registerListener(this);
+ }
+
+ @MainThread
+ final void restartScan() {
+ enforceInitialized();
+
+ if (DEBUG) Log.i(TAG , "restartScan()");
+ if (mBleScanner == null) {
+ if (DEBUG) Log.d(TAG, " > BLE is not available");
+ return;
+ }
+
+ stopScanIfNeeded();
+ startScan();
+ }
+
+ @Override
+ public void onAssociationChanged(@ChangeType int changeType, AssociationInfo association) {
+ // Simply restart scanning.
+ if (Looper.getMainLooper().isCurrentThread()) {
+ restartScan();
+ } else {
+ mMainThreadHandler.post(this::restartScan);
+ }
+ }
+
+ @MainThread
+ private void checkBleState() {
+ enforceInitialized();
+
+ final boolean bleAvailable = isBleAvailable();
+ if (DEBUG) {
+ Log.i(TAG, "checkBleState() bleAvailable=" + bleAvailable);
+ }
+ if ((bleAvailable && mBleScanner != null) || (!bleAvailable && mBleScanner == null)) {
+ // Nothing changed.
+ if (DEBUG) Log.i(TAG, " > BLE status did not change");
+ return;
+ }
+
+ if (bleAvailable) {
+ mBleScanner = mBtAdapter.getBluetoothLeScanner();
+ if (mBleScanner == null) {
+ // Oops, that's a race condition. Can return.
+ return;
+ }
+ if (DEBUG) Log.i(TAG, " > BLE is now available");
+
+ startScan();
+ } else {
+ if (DEBUG) Log.i(TAG, " > BLE is now unavailable");
+
+ stopScanIfNeeded();
+ mBleScanner = null;
+ }
+ }
+
+ /**
+ * A duplicate of {@code BluetoothAdapter.getLeAccess()} method which has the package-private
+ * access level, so it's not accessible from here.
+ */
+ private boolean isBleAvailable() {
+ final int state = mBtAdapter.getLeState();
+ if (DEBUG) Log.d(TAG, "getLeAccess() state=" + nameForBtState(state));
+ return state == STATE_ON || state == STATE_BLE_ON;
+ }
+
+ @MainThread
+ private void startScan() {
+ enforceInitialized();
+ // This method should not be called if scan is already in progress.
+ if (mScanning) throw new IllegalStateException("Scan is already in progress.");
+ // Neither should this method be called if the adapter is not available.
+ if (mBleScanner == null) throw new IllegalStateException("BLE is not available.");
+
+ if (DEBUG) Log.i(TAG, "startScan()");
+
+ // Collect MAC addresses from all associations.
+ final Set<String> macAddresses = new HashSet<>();
+ for (AssociationInfo association : mAssociationStore.getAssociations()) {
+ // Beware that BT stack does not consider low-case MAC addresses valid, while
+ // MacAddress.toString() return a low-case String.
+ final String macAddress = association.getDeviceMacAddressAsString();
+ if (macAddress != null) {
+ macAddresses.add(macAddress);
+ }
+ }
+ if (macAddresses.isEmpty()) {
+ if (DEBUG) Log.i(TAG, " > there are no (associated) devices to Scan for.");
+ return;
+ } else {
+ if (DEBUG) {
+ Log.d(TAG, " > addresses=(n=" + macAddresses.size() + ")"
+ + "[" + String.join(", ", macAddresses) + "]");
+ }
+ }
+
+ final List<ScanFilter> filters = new ArrayList<>(macAddresses.size());
+ for (String macAddress : macAddresses) {
+ final ScanFilter filter = new ScanFilter.Builder()
+ .setDeviceAddress(macAddress)
+ .build();
+ filters.add(filter);
+ }
+
+ mBleScanner.startScan(filters, SCAN_SETTINGS, mScanCallback);
+ mScanning = true;
+ }
+
+ private void stopScanIfNeeded() {
+ enforceInitialized();
+
+ if (DEBUG) Log.i(TAG, "stopScan()");
+ if (!mScanning) {
+ Log.d(TAG, " > not scanning.");
+ return;
+ }
+
+ mBleScanner.stopScan(mScanCallback);
+ mScanning = false;
+ }
+
+ @MainThread
+ private void notifyDeviceFound(@NonNull BluetoothDevice device) {
+ if (DEBUG) Log.i(TAG, "notifyDevice_Found()" + btDeviceToString(device));
+
+ final List<AssociationInfo> associations =
+ mAssociationStore.getAssociationsByAddress(device.getAddress());
+ if (DEBUG) Log.d(TAG, " > associations=" + Arrays.toString(associations.toArray()));
+
+ for (AssociationInfo association : associations) {
+ mCallback.onBleCompanionDeviceFound(association.getId());
+ }
+ }
+
+ @MainThread
+ private void notifyDeviceLost(@NonNull BluetoothDevice device) {
+ if (DEBUG) Log.i(TAG, "notifyDevice_Lost()" + btDeviceToString(device));
+
+ final List<AssociationInfo> associations =
+ mAssociationStore.getAssociationsByAddress(device.getAddress());
+ if (DEBUG) Log.d(TAG, " > associations=" + Arrays.toString(associations.toArray()));
+
+ for (AssociationInfo association : associations) {
+ mCallback.onBleCompanionDeviceLost(association.getId());
+ }
+ }
+
+ private void registerBluetoothStateBroadcastReceiver(Context context) {
+ final BroadcastReceiver receiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final int prevState = intent.getIntExtra(EXTRA_PREVIOUS_STATE, -1);
+ final int state = intent.getIntExtra(EXTRA_STATE, -1);
+
+ if (DEBUG) {
+ // The action is either STATE_CHANGED or BLE_STATE_CHANGED.
+ final String action =
+ intent.getAction().replace("android.bluetooth.adapter.", "bt.");
+ Log.d(TAG, "on(Broadcast)Receive() " + action + ": "
+ + nameForBtState(prevState) + "->" + nameForBtState(state));
+ }
+
+ checkBleState();
+ }
+ };
+
+ final IntentFilter filter = new IntentFilter();
+ filter.addAction(ACTION_STATE_CHANGED);
+ filter.addAction(ACTION_BLE_STATE_CHANGED);
+
+ context.registerReceiver(receiver, filter);
+ }
+
+ private void enforceInitialized() {
+ if (mBtAdapter != null) return;
+ throw new IllegalStateException(getClass().getSimpleName() + " is not initialized");
+ }
+
+ private final ScanCallback mScanCallback = new ScanCallback() {
+ @MainThread
+ @Override
+ public void onScanResult(int callbackType, ScanResult result) {
+ final BluetoothDevice device = result.getDevice();
+
+ if (DEBUG) {
+ Log.d(TAG, "onScanResult() " + nameForBleScanCallbackType(callbackType)
+ + " device=" + btDeviceToString(device));
+ Log.v(TAG, " > scanResult=" + result);
+
+ final List<AssociationInfo> associations =
+ mAssociationStore.getAssociationsByAddress(device.getAddress());
+ Log.v(TAG, " > associations=" + Arrays.toString(associations.toArray()));
+ }
+
+ switch (callbackType) {
+ case CALLBACK_TYPE_FIRST_MATCH:
+ if (mMainThreadHandler.hasNotifyDeviceLostMessages(device)) {
+ mMainThreadHandler.removeNotifyDeviceLostMessages(device);
+ return;
+ }
+
+ notifyDeviceFound(device);
+ break;
+
+ case CALLBACK_TYPE_MATCH_LOST:
+ mMainThreadHandler.sendNotifyDeviceLostDelayedMessage(device);
+ break;
+
+ default:
+ Slog.wtf(TAG, "Unexpected callback "
+ + nameForBleScanCallbackType(callbackType));
+ break;
+ }
+ }
+
+ @MainThread
+ @Override
+ public void onScanFailed(int errorCode) {
+ if (DEBUG) Log.w(TAG, "onScanFailed() " + nameForBleScanErrorCode(errorCode));
+ mScanning = false;
+ }
+ };
+
+ @SuppressLint("HandlerLeak")
+ private class MainThreadHandler extends Handler {
+ private static final int NOTIFY_DEVICE_LOST = 1;
+
+ MainThreadHandler() {
+ super(Looper.getMainLooper());
+ }
+
+ @Override
+ public void handleMessage(@NonNull Message message) {
+ if (message.what != NOTIFY_DEVICE_LOST) return;
+
+ final BluetoothDevice device = (BluetoothDevice) message.obj;
+ notifyDeviceLost(device);
+ }
+
+ void sendNotifyDeviceLostDelayedMessage(BluetoothDevice device) {
+ final Message message = obtainMessage(NOTIFY_DEVICE_LOST, device);
+ sendMessageDelayed(message, NOTIFY_DEVICE_LOST_DELAY);
+ }
+
+ boolean hasNotifyDeviceLostMessages(BluetoothDevice device) {
+ return hasEqualMessages(NOTIFY_DEVICE_LOST, device);
+ }
+
+ void removeNotifyDeviceLostMessages(BluetoothDevice device) {
+ removeEqualMessages(NOTIFY_DEVICE_LOST, device);
+ }
+ }
+
+ private static String nameForBtState(int state) {
+ return nameForState(state) + "(" + state + ")";
+ }
+
+ private static String nameForBleScanCallbackType(int callbackType) {
+ final String name;
+ switch (callbackType) {
+ case CALLBACK_TYPE_ALL_MATCHES:
+ name = "ALL_MATCHES";
+ break;
+ case CALLBACK_TYPE_FIRST_MATCH:
+ name = "FIRST_MATCH";
+ break;
+ case CALLBACK_TYPE_MATCH_LOST:
+ name = "MATCH_LOST";
+ break;
+ default:
+ name = "Unknown";
+ }
+ return name + "(" + callbackType + ")";
+ }
+
+ private static String nameForBleScanErrorCode(int errorCode) {
+ final String name;
+ switch (errorCode) {
+ case SCAN_FAILED_ALREADY_STARTED:
+ name = "ALREADY_STARTED";
+ break;
+ case SCAN_FAILED_APPLICATION_REGISTRATION_FAILED:
+ name = "APPLICATION_REGISTRATION_FAILED";
+ break;
+ case SCAN_FAILED_INTERNAL_ERROR:
+ name = "INTERNAL_ERROR";
+ break;
+ case SCAN_FAILED_FEATURE_UNSUPPORTED:
+ name = "FEATURE_UNSUPPORTED";
+ break;
+ case SCAN_FAILED_OUT_OF_HARDWARE_RESOURCES:
+ name = "OUT_OF_HARDWARE_RESOURCES";
+ break;
+ case SCAN_FAILED_SCANNING_TOO_FREQUENTLY:
+ name = "SCANNING_TOO_FREQUENTLY";
+ break;
+ default:
+ name = "Unknown";
+ }
+ return name + "(" + errorCode + ")";
+ }
+
+ private static final ScanSettings SCAN_SETTINGS = new ScanSettings.Builder()
+ .setCallbackType(CALLBACK_TYPE_FIRST_MATCH | CALLBACK_TYPE_MATCH_LOST)
+ .setScanMode(SCAN_MODE_LOW_POWER)
+ .build();
+}
diff --git a/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java b/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java
new file mode 100644
index 000000000000..dbe866b374f1
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.companion.presence;
+
+import static com.android.server.companion.presence.Utils.btDeviceToString;
+
+import android.annotation.NonNull;
+import android.annotation.SuppressLint;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.companion.AssociationInfo;
+import android.net.MacAddress;
+import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.util.Log;
+
+import com.android.server.companion.AssociationStore;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@SuppressLint("LongLogTag")
+class BluetoothCompanionDeviceConnectionListener
+ extends BluetoothAdapter.BluetoothConnectionCallback
+ implements AssociationStore.OnChangeListener {
+ private static final boolean DEBUG = false;
+ private static final String TAG = "CompanionDevice_PresenceMonitor_BT";
+
+ interface Callback {
+ void onBluetoothCompanionDeviceConnected(int associationId);
+
+ void onBluetoothCompanionDeviceDisconnected(int associationId);
+ }
+
+ private final @NonNull AssociationStore mAssociationStore;
+ private final @NonNull Callback mCallback;
+ /** A set of ALL connected BT device (not only companion.) */
+ private final @NonNull Map<MacAddress, BluetoothDevice> mAllConnectedDevices = new HashMap<>();
+
+ BluetoothCompanionDeviceConnectionListener(@NonNull AssociationStore associationStore,
+ @NonNull Callback callback) {
+ mAssociationStore = associationStore;
+ mCallback = callback;
+ }
+
+ public void init(@NonNull BluetoothAdapter btAdapter) {
+ if (DEBUG) Log.i(TAG, "init()");
+
+ btAdapter.registerBluetoothConnectionCallback(
+ new HandlerExecutor(Handler.getMain()), /* callback */this);
+ mAssociationStore.registerListener(this);
+ }
+
+ /**
+ * Overrides
+ * {@link BluetoothAdapter.BluetoothConnectionCallback#onDeviceConnected(BluetoothDevice)}.
+ */
+ @Override
+ public void onDeviceConnected(@NonNull BluetoothDevice device) {
+ if (DEBUG) Log.i(TAG, "onDevice_Connected() " + btDeviceToString(device));
+
+ final MacAddress macAddress = MacAddress.fromString(device.getAddress());
+ if (mAllConnectedDevices.put(macAddress, device) != null) {
+ if (DEBUG) Log.w(TAG, "Device " + btDeviceToString(device) + " is already connected.");
+ return;
+ }
+
+ onDeviceConnectivityChanged(device, true);
+ }
+
+ /**
+ * Overrides
+ * {@link BluetoothAdapter.BluetoothConnectionCallback#onDeviceConnected(BluetoothDevice)}.
+ * Also invoked when user turns BT off while the device is connected.
+ */
+ @Override
+ public void onDeviceDisconnected(@NonNull BluetoothDevice device,
+ @DisconnectReason int reason) {
+ if (DEBUG) {
+ Log.i(TAG, "onDevice_Disconnected() " + btDeviceToString(device));
+ Log.d(TAG, " reason=" + disconnectReasonText(reason));
+ }
+
+ final MacAddress macAddress = MacAddress.fromString(device.getAddress());
+ if (mAllConnectedDevices.remove(macAddress) == null) {
+ if (DEBUG) {
+ Log.w(TAG, "The device wasn't tracked as connected " + btDeviceToString(device));
+ }
+ return;
+ }
+
+ onDeviceConnectivityChanged(device, false);
+ }
+
+ private void onDeviceConnectivityChanged(@NonNull BluetoothDevice device, boolean connected) {
+ final List<AssociationInfo> associations =
+ mAssociationStore.getAssociationsByAddress(device.getAddress());
+
+ if (DEBUG) {
+ Log.d(TAG, "onDevice_ConnectivityChanged() " + btDeviceToString(device)
+ + " connected=" + connected);
+ if (associations.isEmpty()) {
+ Log.d(TAG, " > No CDM associations");
+ } else {
+ Log.d(TAG, " > associations=" + Arrays.toString(associations.toArray()));
+ }
+ }
+
+ for (AssociationInfo association : associations) {
+ final int id = association.getId();
+ if (connected) {
+ mCallback.onBluetoothCompanionDeviceConnected(id);
+ } else {
+ mCallback.onBluetoothCompanionDeviceDisconnected(id);
+ }
+ }
+ }
+
+ @Override
+ public void onAssociationAdded(AssociationInfo association) {
+ if (DEBUG) Log.d(TAG, "onAssociation_Added() " + association);
+
+ if (mAllConnectedDevices.containsKey(association.getDeviceMacAddress())) {
+ mCallback.onBluetoothCompanionDeviceConnected(association.getId());
+ }
+ }
+
+ @Override
+ public void onAssociationRemoved(AssociationInfo association) {
+ // Intentionally do nothing: CompanionDevicePresenceMonitor will do all the bookkeeping
+ // required.
+ }
+
+ @Override
+ public void onAssociationUpdated(AssociationInfo association, boolean addressChanged) {
+ if (DEBUG) {
+ Log.d(TAG, "onAssociation_Updated() addrChange=" + addressChanged
+ + " " + association);
+ }
+
+ if (!addressChanged) {
+ // Don't need to do anything.
+ return;
+ }
+
+ // At the moment CDM does allow changing association addresses, so we will never come here.
+ // This will be implemented when CDM support updating addresses.
+ throw new IllegalArgumentException("Address changes are not supported.");
+ }
+}
diff --git a/services/companion/java/com/android/server/companion/presence/Utils.java b/services/companion/java/com/android/server/companion/presence/Utils.java
new file mode 100644
index 000000000000..583b443c8cb7
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/presence/Utils.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.companion.presence;
+
+import android.annotation.NonNull;
+import android.bluetooth.BluetoothDevice;
+
+/** Utilities for working with Bluetooth and BLE devices. */
+class Utils {
+
+ /**
+ * @return short String representation of {@link BluetoothDevice}.
+ */
+ static String btDeviceToString(@NonNull BluetoothDevice btDevice) {
+ final StringBuilder sb = new StringBuilder(btDevice.getAddress());
+
+ sb.append(" [name=");
+ final String name = btDevice.getName();
+ if (name != null) {
+ sb.append('\'').append(name).append('\'');
+ } else {
+ sb.append("null");
+ }
+
+ final String alias = btDevice.getAlias();
+ if (alias != null) {
+ sb.append(", alias='").append(alias).append("'");
+ }
+
+ return sb.append(']').toString();
+ }
+
+ private Utils() {
+ }
+}
diff --git a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
index 734e5c3f6f8b..75acf81a4a3c 100644
--- a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
+++ b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
@@ -21,18 +21,24 @@ import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.compat.CompatChanges;
+import android.companion.virtual.VirtualDeviceManager.ActivityListener;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
import android.content.ComponentName;
import android.content.pm.ActivityInfo;
import android.os.Build;
+import android.os.Handler;
+import android.os.Looper;
import android.os.UserHandle;
import android.util.ArraySet;
import android.util.Slog;
+import android.view.Display;
import android.window.DisplayWindowPolicyController;
import java.util.List;
+import java.util.Set;
/**
@@ -49,14 +55,38 @@ class GenericWindowPolicyController extends DisplayWindowPolicyController {
@ChangeId
@EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
public static final long ALLOW_SECURE_ACTIVITY_DISPLAY_ON_REMOTE_DEVICE = 201712607L;
- @NonNull private final ArraySet<UserHandle> mAllowedUsers;
-
- @NonNull final ArraySet<Integer> mRunningUids = new ArraySet<>();
+ @NonNull
+ private final ArraySet<UserHandle> mAllowedUsers;
+ @Nullable
+ private final ArraySet<ComponentName> mAllowedActivities;
+ @Nullable
+ private final ArraySet<ComponentName> mBlockedActivities;
+
+ @NonNull
+ final ArraySet<Integer> mRunningUids = new ArraySet<>();
+ @Nullable private final ActivityListener mActivityListener;
+ private final Handler mHandler = new Handler(Looper.getMainLooper());
+ /**
+ * Creates a window policy controller that is generic to the different use cases of virtual
+ * device.
+ *
+ * @param windowFlags The window flags that this controller is interested in.
+ * @param systemWindowFlags The system window flags that this controller is interested in.
+ * @param allowedUsers The set of users that are allowed to stream in this display.
+ * @param activityListener Activity listener to listen for activity changes. The display ID
+ * is not populated in this callback and is always {@link Display#INVALID_DISPLAY}.
+ */
GenericWindowPolicyController(int windowFlags, int systemWindowFlags,
- @NonNull ArraySet<UserHandle> allowedUsers) {
+ @NonNull ArraySet<UserHandle> allowedUsers,
+ @Nullable Set<ComponentName> allowedActivities,
+ @Nullable Set<ComponentName> blockedActivities,
+ @NonNull ActivityListener activityListener) {
mAllowedUsers = allowedUsers;
+ mAllowedActivities = allowedActivities == null ? null : new ArraySet<>(allowedActivities);
+ mBlockedActivities = blockedActivities == null ? null : new ArraySet<>(blockedActivities);
setInterestedWindowFlags(windowFlags, systemWindowFlags);
+ mActivityListener = activityListener;
}
@Override
@@ -80,13 +110,21 @@ class GenericWindowPolicyController extends DisplayWindowPolicyController {
@Override
public void onTopActivityChanged(ComponentName topActivity, int uid) {
-
+ if (mActivityListener != null) {
+ // Post callback on the main thread so it doesn't block activity launching
+ mHandler.post(() ->
+ mActivityListener.onTopActivityChanged(Display.INVALID_DISPLAY, topActivity));
+ }
}
@Override
public void onRunningAppsChanged(ArraySet<Integer> runningUids) {
mRunningUids.clear();
mRunningUids.addAll(runningUids);
+ if (mActivityListener != null && mRunningUids.isEmpty()) {
+ // Post callback on the main thread so it doesn't block activity launching
+ mHandler.post(() -> mActivityListener.onDisplayEmpty(Display.INVALID_DISPLAY));
+ }
}
/**
@@ -108,6 +146,18 @@ class GenericWindowPolicyController extends DisplayWindowPolicyController {
Slog.d(TAG, "Virtual device activity not allowed from user " + activityUser);
return false;
}
+ if (mBlockedActivities != null
+ && mBlockedActivities.contains(activityInfo.getComponentName())) {
+ Slog.d(TAG,
+ "Virtual device blocking launch of " + activityInfo.getComponentName());
+ return false;
+ }
+ if (mAllowedActivities != null
+ && !mAllowedActivities.contains(activityInfo.getComponentName())) {
+ Slog.d(TAG,
+ activityInfo.getComponentName() + " is not in the allowed list.");
+ return false;
+ }
if (!CompatChanges.isChangeEnabled(ALLOW_SECURE_ACTIVITY_DISPLAY_ON_REMOTE_DEVICE,
activityInfo.packageName, activityUser)) {
// TODO(b/201712607): Add checks for the apps that use SurfaceView#setSecure.
diff --git a/services/companion/java/com/android/server/companion/virtual/InputController.java b/services/companion/java/com/android/server/companion/virtual/InputController.java
index 067edcc0b08d..ae39d7ef0b83 100644
--- a/services/companion/java/com/android/server/companion/virtual/InputController.java
+++ b/services/companion/java/com/android/server/companion/virtual/InputController.java
@@ -16,8 +16,11 @@
package com.android.server.companion.virtual;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.graphics.Point;
+import android.graphics.PointF;
+import android.hardware.input.InputManagerInternal;
import android.hardware.input.VirtualKeyEvent;
import android.hardware.input.VirtualMouseButtonEvent;
import android.hardware.input.VirtualMouseRelativeEvent;
@@ -26,25 +29,40 @@ import android.hardware.input.VirtualTouchEvent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.ArrayMap;
+import android.util.Slog;
+import android.view.Display;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.LocalServices;
import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Map;
+import java.util.concurrent.atomic.AtomicLong;
/** Controls virtual input devices, including device lifecycle and event dispatch. */
class InputController {
+ private static final String TAG = "VirtualInputController";
+
private final Object mLock;
/* Token -> file descriptor associations. */
@VisibleForTesting
@GuardedBy("mLock")
- final Map<IBinder, Integer> mInputDeviceFds = new ArrayMap<>();
+ final Map<IBinder, InputDeviceDescriptor> mInputDeviceDescriptors = new ArrayMap<>();
private final NativeWrapper mNativeWrapper;
+ /**
+ * Because the pointer is a singleton, it can only be targeted at one display at a time. Because
+ * multiple mice could be concurrently registered, mice that are associated with a different
+ * display than the current target display should not be allowed to affect the current target.
+ */
+ @VisibleForTesting int mActivePointerDisplayId;
+
InputController(@NonNull Object lock) {
this(lock, new NativeWrapper());
}
@@ -53,32 +71,39 @@ class InputController {
InputController(@NonNull Object lock, @NonNull NativeWrapper nativeWrapper) {
mLock = lock;
mNativeWrapper = nativeWrapper;
+ mActivePointerDisplayId = Display.INVALID_DISPLAY;
}
void close() {
synchronized (mLock) {
- for (int fd : mInputDeviceFds.values()) {
- mNativeWrapper.closeUinput(fd);
+ for (InputDeviceDescriptor inputDeviceDescriptor : mInputDeviceDescriptors.values()) {
+ mNativeWrapper.closeUinput(inputDeviceDescriptor.getFileDescriptor());
}
- mInputDeviceFds.clear();
+ mInputDeviceDescriptors.clear();
+ resetMouseValuesLocked();
}
}
void createKeyboard(@NonNull String deviceName,
int vendorId,
int productId,
- @NonNull IBinder deviceToken) {
+ @NonNull IBinder deviceToken,
+ int displayId) {
final int fd = mNativeWrapper.openUinputKeyboard(deviceName, vendorId, productId);
if (fd < 0) {
throw new RuntimeException(
"A native error occurred when creating keyboard: " + -fd);
}
+ final BinderDeathRecipient binderDeathRecipient = new BinderDeathRecipient(deviceToken);
synchronized (mLock) {
- mInputDeviceFds.put(deviceToken, fd);
+ mInputDeviceDescriptors.put(deviceToken,
+ new InputDeviceDescriptor(fd, binderDeathRecipient,
+ InputDeviceDescriptor.TYPE_KEYBOARD, displayId));
}
try {
- deviceToken.linkToDeath(new BinderDeathRecipient(deviceToken), /* flags= */ 0);
+ deviceToken.linkToDeath(binderDeathRecipient, /* flags= */ 0);
} catch (RemoteException e) {
+ // TODO(b/215608394): remove and close InputDeviceDescriptor
throw new RuntimeException("Could not create virtual keyboard", e);
}
}
@@ -86,18 +111,27 @@ class InputController {
void createMouse(@NonNull String deviceName,
int vendorId,
int productId,
- @NonNull IBinder deviceToken) {
+ @NonNull IBinder deviceToken,
+ int displayId) {
final int fd = mNativeWrapper.openUinputMouse(deviceName, vendorId, productId);
if (fd < 0) {
throw new RuntimeException(
"A native error occurred when creating mouse: " + -fd);
}
+ final BinderDeathRecipient binderDeathRecipient = new BinderDeathRecipient(deviceToken);
synchronized (mLock) {
- mInputDeviceFds.put(deviceToken, fd);
+ mInputDeviceDescriptors.put(deviceToken,
+ new InputDeviceDescriptor(fd, binderDeathRecipient,
+ InputDeviceDescriptor.TYPE_MOUSE, displayId));
+ final InputManagerInternal inputManagerInternal =
+ LocalServices.getService(InputManagerInternal.class);
+ inputManagerInternal.setVirtualMousePointerDisplayId(displayId);
+ mActivePointerDisplayId = displayId;
}
try {
- deviceToken.linkToDeath(new BinderDeathRecipient(deviceToken), /* flags= */ 0);
+ deviceToken.linkToDeath(binderDeathRecipient, /* flags= */ 0);
} catch (RemoteException e) {
+ // TODO(b/215608394): remove and close InputDeviceDescriptor
throw new RuntimeException("Could not create virtual mouse", e);
}
}
@@ -106,6 +140,7 @@ class InputController {
int vendorId,
int productId,
@NonNull IBinder deviceToken,
+ int displayId,
@NonNull Point screenSize) {
final int fd = mNativeWrapper.openUinputTouchscreen(deviceName, vendorId, productId,
screenSize.y, screenSize.x);
@@ -113,93 +148,177 @@ class InputController {
throw new RuntimeException(
"A native error occurred when creating touchscreen: " + -fd);
}
+ final BinderDeathRecipient binderDeathRecipient = new BinderDeathRecipient(deviceToken);
synchronized (mLock) {
- mInputDeviceFds.put(deviceToken, fd);
+ mInputDeviceDescriptors.put(deviceToken,
+ new InputDeviceDescriptor(fd, binderDeathRecipient,
+ InputDeviceDescriptor.TYPE_TOUCHSCREEN, displayId));
}
try {
- deviceToken.linkToDeath(new BinderDeathRecipient(deviceToken), /* flags= */ 0);
+ deviceToken.linkToDeath(binderDeathRecipient, /* flags= */ 0);
} catch (RemoteException e) {
+ // TODO(b/215608394): remove and close InputDeviceDescriptor
throw new RuntimeException("Could not create virtual touchscreen", e);
}
}
void unregisterInputDevice(@NonNull IBinder token) {
synchronized (mLock) {
- final Integer fd = mInputDeviceFds.remove(token);
- if (fd == null) {
+ final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.remove(
+ token);
+ if (inputDeviceDescriptor == null) {
throw new IllegalArgumentException(
"Could not unregister input device for given token");
}
- mNativeWrapper.closeUinput(fd);
+ token.unlinkToDeath(inputDeviceDescriptor.getDeathRecipient(), /* flags= */ 0);
+ mNativeWrapper.closeUinput(inputDeviceDescriptor.getFileDescriptor());
+
+ // Reset values to the default if all virtual mice are unregistered, or set display
+ // id if there's another mouse (choose the most recent).
+ if (inputDeviceDescriptor.isMouse()) {
+ updateMouseValuesLocked();
+ }
}
}
+ @GuardedBy("mLock")
+ private void updateMouseValuesLocked() {
+ InputDeviceDescriptor mostRecentlyCreatedMouse = null;
+ for (InputDeviceDescriptor otherInputDeviceDescriptor :
+ mInputDeviceDescriptors.values()) {
+ if (otherInputDeviceDescriptor.isMouse()) {
+ if (mostRecentlyCreatedMouse == null
+ || (otherInputDeviceDescriptor.getCreationOrderNumber()
+ > mostRecentlyCreatedMouse.getCreationOrderNumber())) {
+ mostRecentlyCreatedMouse = otherInputDeviceDescriptor;
+ }
+ }
+ }
+ if (mostRecentlyCreatedMouse != null) {
+ final InputManagerInternal inputManagerInternal =
+ LocalServices.getService(InputManagerInternal.class);
+ inputManagerInternal.setVirtualMousePointerDisplayId(
+ mostRecentlyCreatedMouse.getDisplayId());
+ mActivePointerDisplayId = mostRecentlyCreatedMouse.getDisplayId();
+ } else {
+ // All mice have been unregistered; reset all values.
+ resetMouseValuesLocked();
+ }
+ }
+
+ private void resetMouseValuesLocked() {
+ final InputManagerInternal inputManagerInternal =
+ LocalServices.getService(InputManagerInternal.class);
+ inputManagerInternal.setVirtualMousePointerDisplayId(Display.INVALID_DISPLAY);
+ mActivePointerDisplayId = Display.INVALID_DISPLAY;
+ }
+
boolean sendKeyEvent(@NonNull IBinder token, @NonNull VirtualKeyEvent event) {
synchronized (mLock) {
- final Integer fd = mInputDeviceFds.get(token);
- if (fd == null) {
+ final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.get(
+ token);
+ if (inputDeviceDescriptor == null) {
throw new IllegalArgumentException(
"Could not send key event to input device for given token");
}
- return mNativeWrapper.writeKeyEvent(fd, event.getKeyCode(), event.getAction());
+ return mNativeWrapper.writeKeyEvent(inputDeviceDescriptor.getFileDescriptor(),
+ event.getKeyCode(), event.getAction());
}
}
boolean sendButtonEvent(@NonNull IBinder token, @NonNull VirtualMouseButtonEvent event) {
synchronized (mLock) {
- final Integer fd = mInputDeviceFds.get(token);
- if (fd == null) {
+ final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.get(
+ token);
+ if (inputDeviceDescriptor == null) {
throw new IllegalArgumentException(
"Could not send button event to input device for given token");
}
- return mNativeWrapper.writeButtonEvent(fd, event.getButtonCode(), event.getAction());
+ if (inputDeviceDescriptor.getDisplayId() != mActivePointerDisplayId) {
+ throw new IllegalStateException(
+ "Display id associated with this mouse is not currently targetable");
+ }
+ return mNativeWrapper.writeButtonEvent(inputDeviceDescriptor.getFileDescriptor(),
+ event.getButtonCode(), event.getAction());
}
}
boolean sendTouchEvent(@NonNull IBinder token, @NonNull VirtualTouchEvent event) {
synchronized (mLock) {
- final Integer fd = mInputDeviceFds.get(token);
- if (fd == null) {
+ final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.get(
+ token);
+ if (inputDeviceDescriptor == null) {
throw new IllegalArgumentException(
"Could not send touch event to input device for given token");
}
- return mNativeWrapper.writeTouchEvent(fd, event.getPointerId(), event.getToolType(),
- event.getAction(), event.getX(), event.getY(), event.getPressure(),
- event.getMajorAxisSize());
+ return mNativeWrapper.writeTouchEvent(inputDeviceDescriptor.getFileDescriptor(),
+ event.getPointerId(), event.getToolType(), event.getAction(), event.getX(),
+ event.getY(), event.getPressure(), event.getMajorAxisSize());
}
}
boolean sendRelativeEvent(@NonNull IBinder token, @NonNull VirtualMouseRelativeEvent event) {
synchronized (mLock) {
- final Integer fd = mInputDeviceFds.get(token);
- if (fd == null) {
+ final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.get(
+ token);
+ if (inputDeviceDescriptor == null) {
throw new IllegalArgumentException(
"Could not send relative event to input device for given token");
}
- return mNativeWrapper.writeRelativeEvent(fd, event.getRelativeX(),
- event.getRelativeY());
+ if (inputDeviceDescriptor.getDisplayId() != mActivePointerDisplayId) {
+ throw new IllegalStateException(
+ "Display id associated with this mouse is not currently targetable");
+ }
+ return mNativeWrapper.writeRelativeEvent(inputDeviceDescriptor.getFileDescriptor(),
+ event.getRelativeX(), event.getRelativeY());
}
}
boolean sendScrollEvent(@NonNull IBinder token, @NonNull VirtualMouseScrollEvent event) {
synchronized (mLock) {
- final Integer fd = mInputDeviceFds.get(token);
- if (fd == null) {
+ final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.get(
+ token);
+ if (inputDeviceDescriptor == null) {
throw new IllegalArgumentException(
"Could not send scroll event to input device for given token");
}
- return mNativeWrapper.writeScrollEvent(fd, event.getXAxisMovement(),
- event.getYAxisMovement());
+ if (inputDeviceDescriptor.getDisplayId() != mActivePointerDisplayId) {
+ throw new IllegalStateException(
+ "Display id associated with this mouse is not currently targetable");
+ }
+ return mNativeWrapper.writeScrollEvent(inputDeviceDescriptor.getFileDescriptor(),
+ event.getXAxisMovement(), event.getYAxisMovement());
+ }
+ }
+
+ public PointF getCursorPosition(@NonNull IBinder token) {
+ synchronized (mLock) {
+ final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.get(
+ token);
+ if (inputDeviceDescriptor == null) {
+ throw new IllegalArgumentException(
+ "Could not get cursor position for input device for given token");
+ }
+ if (inputDeviceDescriptor.getDisplayId() != mActivePointerDisplayId) {
+ throw new IllegalStateException(
+ "Display id associated with this mouse is not currently targetable");
+ }
+ return LocalServices.getService(InputManagerInternal.class).getCursorPosition();
}
}
public void dump(@NonNull PrintWriter fout) {
fout.println(" InputController: ");
synchronized (mLock) {
- fout.println(" Active file descriptors: ");
- for (int inputDeviceFd : mInputDeviceFds.values()) {
- fout.println(inputDeviceFd);
+ fout.println(" Active descriptors: ");
+ for (InputDeviceDescriptor inputDeviceDescriptor : mInputDeviceDescriptors.values()) {
+ fout.println(" fd: " + inputDeviceDescriptor.getFileDescriptor());
+ fout.println(" displayId: " + inputDeviceDescriptor.getDisplayId());
+ fout.println(" creationOrder: "
+ + inputDeviceDescriptor.getCreationOrderNumber());
+ fout.println(" type: " + inputDeviceDescriptor.getType());
}
+ fout.println(" Active mouse display id: " + mActivePointerDisplayId);
}
}
@@ -267,6 +386,63 @@ class InputController {
}
}
+ @VisibleForTesting static final class InputDeviceDescriptor {
+
+ static final int TYPE_KEYBOARD = 1;
+ static final int TYPE_MOUSE = 2;
+ static final int TYPE_TOUCHSCREEN = 3;
+ @IntDef(prefix = { "TYPE_" }, value = {
+ TYPE_KEYBOARD,
+ TYPE_MOUSE,
+ TYPE_TOUCHSCREEN,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface Type {
+ }
+
+ private static final AtomicLong sNextCreationOrderNumber = new AtomicLong(1);
+
+ private final int mFd;
+ private final IBinder.DeathRecipient mDeathRecipient;
+ private final @Type int mType;
+ private final int mDisplayId;
+ // Monotonically increasing number; devices with lower numbers were created earlier.
+ private final long mCreationOrderNumber;
+
+ InputDeviceDescriptor(int fd, IBinder.DeathRecipient deathRecipient,
+ @Type int type, int displayId) {
+ mFd = fd;
+ mDeathRecipient = deathRecipient;
+ mType = type;
+ mDisplayId = displayId;
+ mCreationOrderNumber = sNextCreationOrderNumber.getAndIncrement();
+ }
+
+ public int getFileDescriptor() {
+ return mFd;
+ }
+
+ public int getType() {
+ return mType;
+ }
+
+ public boolean isMouse() {
+ return mType == TYPE_MOUSE;
+ }
+
+ public IBinder.DeathRecipient getDeathRecipient() {
+ return mDeathRecipient;
+ }
+
+ public int getDisplayId() {
+ return mDisplayId;
+ }
+
+ public long getCreationOrderNumber() {
+ return mCreationOrderNumber;
+ }
+ }
+
private final class BinderDeathRecipient implements IBinder.DeathRecipient {
private final IBinder mDeviceToken;
@@ -277,6 +453,10 @@ class InputController {
@Override
public void binderDied() {
+ // All callers are expected to call {@link VirtualDevice#unregisterInputDevice} before
+ // quitting, which removes this death recipient. If this is invoked, the remote end
+ // died, or they disposed of the object without properly unregistering.
+ Slog.e(TAG, "Virtual input controller binder died");
unregisterInputDevice(mDeviceToken);
}
}
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index 0c0ee521af0f..5c8fb2ecec0f 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -29,10 +29,15 @@ import android.app.PendingIntent;
import android.app.admin.DevicePolicyManager;
import android.companion.AssociationInfo;
import android.companion.virtual.IVirtualDevice;
+import android.companion.virtual.IVirtualDeviceActivityListener;
+import android.companion.virtual.VirtualDeviceManager.ActivityListener;
import android.companion.virtual.VirtualDeviceParams;
+import android.content.ComponentName;
import android.content.Context;
import android.graphics.Point;
+import android.graphics.PointF;
import android.hardware.display.DisplayManager;
+import android.hardware.input.InputManagerInternal;
import android.hardware.input.VirtualKeyEvent;
import android.hardware.input.VirtualMouseButtonEvent;
import android.hardware.input.VirtualMouseRelativeEvent;
@@ -50,11 +55,11 @@ import android.util.SparseArray;
import android.window.DisplayWindowPolicyController;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.LocalServices;
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.Set;
final class VirtualDeviceImpl extends IVirtualDevice.Stub
@@ -69,10 +74,34 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
private final int mOwnerUid;
private final InputController mInputController;
@VisibleForTesting
- final List<Integer> mVirtualDisplayIds = new ArrayList<>();
+ final Set<Integer> mVirtualDisplayIds = new ArraySet<>();
private final OnDeviceCloseListener mListener;
private final IBinder mAppToken;
private final VirtualDeviceParams mParams;
+ private final IVirtualDeviceActivityListener mActivityListener;
+
+ private ActivityListener createListenerAdapter(int displayId) {
+ return new ActivityListener() {
+
+ @Override
+ public void onTopActivityChanged(int unusedDisplayId, ComponentName topActivity) {
+ try {
+ mActivityListener.onTopActivityChanged(displayId, topActivity);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Unable to call mActivityListener", e);
+ }
+ }
+
+ @Override
+ public void onDisplayEmpty(int unusedDisplayId) {
+ try {
+ mActivityListener.onDisplayEmpty(displayId);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Unable to call mActivityListener", e);
+ }
+ }
+ };
+ }
/**
* A mapping from the virtual display ID to its corresponding
@@ -83,18 +112,22 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
VirtualDeviceImpl(Context context, AssociationInfo associationInfo,
IBinder token, int ownerUid, OnDeviceCloseListener listener,
- PendingTrampolineCallback pendingTrampolineCallback, VirtualDeviceParams params) {
+ PendingTrampolineCallback pendingTrampolineCallback,
+ IVirtualDeviceActivityListener activityListener,
+ VirtualDeviceParams params) {
this(context, associationInfo, token, ownerUid, /* inputController= */ null, listener,
- pendingTrampolineCallback, params);
+ pendingTrampolineCallback, activityListener, params);
}
@VisibleForTesting
VirtualDeviceImpl(Context context, AssociationInfo associationInfo, IBinder token,
int ownerUid, InputController inputController, OnDeviceCloseListener listener,
- PendingTrampolineCallback pendingTrampolineCallback, VirtualDeviceParams params) {
+ PendingTrampolineCallback pendingTrampolineCallback,
+ IVirtualDeviceActivityListener activityListener, VirtualDeviceParams params) {
mContext = context;
mAssociationInfo = associationInfo;
mPendingTrampolineCallback = pendingTrampolineCallback;
+ mActivityListener = activityListener;
mOwnerUid = ownerUid;
mAppToken = token;
mParams = params;
@@ -202,7 +235,8 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
}
final long token = Binder.clearCallingIdentity();
try {
- mInputController.createKeyboard(deviceName, vendorId, productId, deviceToken);
+ mInputController.createKeyboard(deviceName, vendorId, productId, deviceToken,
+ displayId);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -227,7 +261,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
}
final long token = Binder.clearCallingIdentity();
try {
- mInputController.createMouse(deviceName, vendorId, productId, deviceToken);
+ mInputController.createMouse(deviceName, vendorId, productId, deviceToken, displayId);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -254,7 +288,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
final long token = Binder.clearCallingIdentity();
try {
mInputController.createTouchscreen(deviceName, vendorId, productId,
- deviceToken, screenSize);
+ deviceToken, displayId, screenSize);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -324,10 +358,21 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
}
}
+ @Override // Binder call
+ public PointF getCursorPosition(IBinder token) {
+ final long binderToken = Binder.clearCallingIdentity();
+ try {
+ return mInputController.getCursorPosition(token);
+ } finally {
+ Binder.restoreCallingIdentity(binderToken);
+ }
+ }
+
@Override
protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
fout.println(" VirtualDevice: ");
fout.println(" mAssociationId: " + mAssociationInfo.getId());
+ fout.println(" mParams: " + mParams);
fout.println(" mVirtualDisplayIds: ");
synchronized (mVirtualDeviceLock) {
for (int id : mVirtualDisplayIds) {
@@ -343,9 +388,15 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
"Virtual device already have a virtual display with ID " + displayId);
}
mVirtualDisplayIds.add(displayId);
+ LocalServices.getService(InputManagerInternal.class).setDisplayEligibilityForPointerCapture(
+ displayId, false);
final GenericWindowPolicyController dwpc =
new GenericWindowPolicyController(FLAG_SECURE,
- SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS, getAllowedUserHandles());
+ SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS,
+ getAllowedUserHandles(),
+ mParams.getAllowedActivities(),
+ mParams.getBlockedActivities(),
+ createListenerAdapter(displayId));
mWindowPolicyControllers.put(displayId, dwpc);
return dwpc;
}
@@ -374,6 +425,8 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
"Virtual device doesn't have a virtual display with ID " + displayId);
}
mVirtualDisplayIds.remove(displayId);
+ LocalServices.getService(InputManagerInternal.class).setDisplayEligibilityForPointerCapture(
+ displayId, true);
mWindowPolicyControllers.remove(displayId);
}
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
index 7e0c2fc37da6..b507110d5473 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -27,6 +27,7 @@ import android.companion.AssociationInfo;
import android.companion.CompanionDeviceManager;
import android.companion.CompanionDeviceManager.OnAssociationsChangedListener;
import android.companion.virtual.IVirtualDevice;
+import android.companion.virtual.IVirtualDeviceActivityListener;
import android.companion.virtual.IVirtualDeviceManager;
import android.companion.virtual.VirtualDeviceParams;
import android.content.Context;
@@ -176,7 +177,8 @@ public class VirtualDeviceManagerService extends SystemService {
IBinder token,
String packageName,
int associationId,
- @NonNull VirtualDeviceParams params) {
+ @NonNull VirtualDeviceParams params,
+ @NonNull IVirtualDeviceActivityListener activityListener) {
getContext().enforceCallingOrSelfPermission(
android.Manifest.permission.CREATE_VIRTUAL_DEVICE,
"createVirtualDevice");
@@ -206,7 +208,7 @@ public class VirtualDeviceManagerService extends SystemService {
}
}
},
- this, params);
+ this, activityListener, params);
mVirtualDevices.put(associationInfo.getId(), virtualDevice);
return virtualDevice;
}
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 094ed375325e..1106fe7270e6 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -176,6 +176,9 @@ java_library_static {
"overlayable_policy_aidl-java",
"SurfaceFlingerProperties",
"com.android.sysprop.watchdog",
+ // This is used for services.connectivity-tiramisu-sources.
+ // TODO: delete when NetworkStatsService is moved to the mainline module.
+ "net-utils-device-common-bpf",
],
javac_shard_size: 50,
}
diff --git a/services/core/java/android/app/usage/OWNERS b/services/core/java/android/app/usage/OWNERS
new file mode 100644
index 000000000000..3a555143b11d
--- /dev/null
+++ b/services/core/java/android/app/usage/OWNERS
@@ -0,0 +1 @@
+include platform/frameworks/base:/core/java/android/app/usage/OWNERS
diff --git a/services/core/java/android/app/usage/UsageStatsManagerInternal.java b/services/core/java/android/app/usage/UsageStatsManagerInternal.java
index 21fc19ec3079..435d294a3e8e 100644
--- a/services/core/java/android/app/usage/UsageStatsManagerInternal.java
+++ b/services/core/java/android/app/usage/UsageStatsManagerInternal.java
@@ -17,6 +17,7 @@
package android.app.usage;
import android.annotation.CurrentTimeMillisLong;
+import android.annotation.ElapsedRealtimeLong;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -25,6 +26,7 @@ import android.content.ComponentName;
import android.content.LocusId;
import android.content.res.Configuration;
import android.os.IBinder;
+import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
@@ -362,4 +364,52 @@ public abstract class UsageStatsManagerInternal {
/** Unregister a listener from being notified of every estimated launch time change. */
public abstract void unregisterLaunchTimeChangedListener(
@NonNull EstimatedLaunchTimeChangedListener listener);
+
+ /**
+ * Reports a broadcast dispatched event to the UsageStatsManager.
+ *
+ * @param sourceUid uid of the package that sent the broadcast.
+ * @param targetPackage name of the package that the broadcast is targeted to.
+ * @param targetUser user that {@code targetPackage} belongs to.
+ * @param idForResponseEvent ID to be used for recording any response events corresponding
+ * to this broadcast.
+ * @param timestampMs time (in millis) when the broadcast was dispatched, in
+ * {@link SystemClock#elapsedRealtime()} timebase.
+ */
+ public abstract void reportBroadcastDispatched(int sourceUid, @NonNull String targetPackage,
+ @NonNull UserHandle targetUser, long idForResponseEvent,
+ @ElapsedRealtimeLong long timestampMs);
+
+ /**
+ * Reports a notification posted event to the UsageStatsManager.
+ *
+ * @param packageName name of the package which posted the notification.
+ * @param user user that {@code packageName} belongs to.
+ * @param timestampMs time (in millis) when the notification was posted, in
+ * {@link SystemClock#elapsedRealtime()} timebase.
+ */
+ public abstract void reportNotificationPosted(@NonNull String packageName,
+ @NonNull UserHandle user, @ElapsedRealtimeLong long timestampMs);
+
+ /**
+ * Reports a notification updated event to the UsageStatsManager.
+ *
+ * @param packageName name of the package which updated the notification.
+ * @param user user that {@code packageName} belongs to.
+ * @param timestampMs time (in millis) when the notification was updated, in
+ * {@link SystemClock#elapsedRealtime()} timebase.
+ */
+ public abstract void reportNotificationUpdated(@NonNull String packageName,
+ @NonNull UserHandle user, @ElapsedRealtimeLong long timestampMs);
+
+ /**
+ * Reports a notification removed event to the UsageStatsManager.
+ *
+ * @param packageName name of the package which removed the notification.
+ * @param user user that {@code packageName} belongs to.
+ * @param timestampMs time (in millis) when the notification was removed, in
+ * {@link SystemClock#elapsedRealtime()} timebase.
+ */
+ public abstract void reportNotificationRemoved(@NonNull String packageName,
+ @NonNull UserHandle user, @ElapsedRealtimeLong long timestampMs);
}
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 60cae4d67f5a..f56bfab7055f 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -70,6 +70,7 @@ public abstract class PackageManagerInternal implements PackageSettingsSnapshotP
PACKAGE_SYSTEM,
PACKAGE_SETUP_WIZARD,
PACKAGE_INSTALLER,
+ PACKAGE_UNINSTALLER,
PACKAGE_VERIFIER,
PACKAGE_BROWSER,
PACKAGE_SYSTEM_TEXT_CLASSIFIER,
@@ -89,23 +90,25 @@ public abstract class PackageManagerInternal implements PackageSettingsSnapshotP
public static final int PACKAGE_SYSTEM = 0;
public static final int PACKAGE_SETUP_WIZARD = 1;
public static final int PACKAGE_INSTALLER = 2;
- public static final int PACKAGE_VERIFIER = 3;
- public static final int PACKAGE_BROWSER = 4;
- public static final int PACKAGE_SYSTEM_TEXT_CLASSIFIER = 5;
- public static final int PACKAGE_PERMISSION_CONTROLLER = 6;
- public static final int PACKAGE_WELLBEING = 7;
- public static final int PACKAGE_DOCUMENTER = 8;
- public static final int PACKAGE_CONFIGURATOR = 9;
- public static final int PACKAGE_INCIDENT_REPORT_APPROVER = 10;
- public static final int PACKAGE_APP_PREDICTOR = 11;
- public static final int PACKAGE_OVERLAY_CONFIG_SIGNATURE = 12;
- public static final int PACKAGE_WIFI = 13;
- public static final int PACKAGE_COMPANION = 14;
- public static final int PACKAGE_RETAIL_DEMO = 15;
- public static final int PACKAGE_RECENTS = 16;
+ public static final int PACKAGE_UNINSTALLER = 3;
+ public static final int PACKAGE_VERIFIER = 4;
+ public static final int PACKAGE_BROWSER = 5;
+ public static final int PACKAGE_SYSTEM_TEXT_CLASSIFIER = 6;
+ public static final int PACKAGE_PERMISSION_CONTROLLER = 7;
+ public static final int PACKAGE_WELLBEING = 8;
+ public static final int PACKAGE_DOCUMENTER = 9;
+ public static final int PACKAGE_CONFIGURATOR = 10;
+ public static final int PACKAGE_INCIDENT_REPORT_APPROVER = 11;
+ public static final int PACKAGE_APP_PREDICTOR = 12;
+ public static final int PACKAGE_OVERLAY_CONFIG_SIGNATURE = 13;
+ public static final int PACKAGE_WIFI = 14;
+ public static final int PACKAGE_COMPANION = 15;
+ public static final int PACKAGE_RETAIL_DEMO = 16;
+ public static final int PACKAGE_RECENTS = 17;
+ public static final int PACKAGE_AMBIENT_CONTEXT_DETECTION = 18;
// Integer value of the last known package ID. Increases as new ID is added to KnownPackage.
// Please note the numbers should be continuous.
- public static final int LAST_KNOWN_PACKAGE = PACKAGE_RECENTS;
+ public static final int LAST_KNOWN_PACKAGE = PACKAGE_AMBIENT_CONTEXT_DETECTION;
@LongDef(flag = true, prefix = "RESOLVE_", value = {
RESOLVE_NON_BROWSER_ONLY,
@@ -1141,6 +1144,8 @@ public abstract class PackageManagerInternal implements PackageSettingsSnapshotP
return "Setup Wizard";
case PACKAGE_INSTALLER:
return "Installer";
+ case PACKAGE_UNINSTALLER:
+ return "Uninstaller";
case PACKAGE_VERIFIER:
return "Verifier";
case PACKAGE_BROWSER:
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index 844ac86e8eb5..5d48d7821cad 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -853,7 +853,9 @@ public final class BatteryService extends SystemService {
pw.println("Battery service (battery) commands:");
pw.println(" help");
pw.println(" Print this help text.");
- pw.println(" set [-f] [ac|usb|wireless|status|level|temp|present|invalid] <value>");
+ pw.println(" get [-f] [ac|usb|wireless|status|level|temp|present|counter|invalid]");
+ pw.println(
+ " set [-f] [ac|usb|wireless|status|level|temp|present|counter|invalid] <value>");
pw.println(" Force a battery property value, freezing battery state.");
pw.println(" -f: force a battery change broadcast be sent, prints new sequence.");
pw.println(" unplug [-f]");
@@ -863,7 +865,7 @@ public final class BatteryService extends SystemService {
pw.println(" Unfreeze battery state, returning to current hardware values.");
pw.println(" -f: force a battery change broadcast be sent, prints new sequence.");
if (Build.IS_DEBUGGABLE) {
- pw.println(" disable_charge");
+ pw.println(" suspend_input");
pw.println(" Suspend charging even if plugged in. ");
}
}
@@ -893,6 +895,46 @@ public final class BatteryService extends SystemService {
android.Manifest.permission.DEVICE_POWER, null);
unplugBattery(/* forceUpdate= */ (opts & OPTION_FORCE_UPDATE) != 0, pw);
} break;
+ case "get": {
+ final String key = shell.getNextArg();
+ if (key == null) {
+ pw.println("No property specified");
+ return -1;
+
+ }
+ switch (key) {
+ case "present":
+ pw.println(mHealthInfo.batteryPresent);
+ break;
+ case "ac":
+ pw.println(mHealthInfo.chargerAcOnline);
+ break;
+ case "usb":
+ pw.println(mHealthInfo.chargerUsbOnline);
+ break;
+ case "wireless":
+ pw.println(mHealthInfo.chargerWirelessOnline);
+ break;
+ case "status":
+ pw.println(mHealthInfo.batteryStatus);
+ break;
+ case "level":
+ pw.println(mHealthInfo.batteryLevel);
+ break;
+ case "counter":
+ pw.println(mHealthInfo.batteryChargeCounterUah);
+ break;
+ case "temp":
+ pw.println(mHealthInfo.batteryTemperatureTenthsCelsius);
+ break;
+ case "invalid":
+ pw.println(mInvalidCharger);
+ break;
+ default:
+ pw.println("Unknown get option: " + key);
+ break;
+ }
+ } break;
case "set": {
int opts = parseOptions(shell);
getContext().enforceCallingOrSelfPermission(
diff --git a/services/core/java/com/android/server/MasterClearReceiver.java b/services/core/java/com/android/server/MasterClearReceiver.java
index 62dc27330f87..be2b7f72bd4f 100644
--- a/services/core/java/com/android/server/MasterClearReceiver.java
+++ b/services/core/java/com/android/server/MasterClearReceiver.java
@@ -16,11 +16,14 @@
package com.android.server;
+import static android.app.admin.DevicePolicyResources.Strings.Core.WORK_PROFILE_DELETED_TITLE;
+
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.ProgressDialog;
+import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -155,7 +158,7 @@ public class MasterClearReceiver extends BroadcastReceiver {
final Notification notification =
new Notification.Builder(context, SystemNotificationChannels.DEVICE_ADMIN)
.setSmallIcon(android.R.drawable.stat_sys_warning)
- .setContentTitle(context.getString(R.string.work_profile_deleted))
+ .setContentTitle(getWorkProfileDeletedTitle(context))
.setContentText(wipeReason)
.setColor(context.getColor(R.color.system_notification_accent_color))
.setStyle(new Notification.BigTextStyle().bigText(wipeReason))
@@ -164,6 +167,12 @@ public class MasterClearReceiver extends BroadcastReceiver {
SystemMessageProto.SystemMessage.NOTE_PROFILE_WIPED, notification);
}
+ private String getWorkProfileDeletedTitle(Context context) {
+ final DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
+ return dpm.getString(WORK_PROFILE_DELETED_TITLE,
+ () -> context.getString(R.string.work_profile_deleted));
+ }
+
private @UserIdInt int getCurrentForegroundUserId() {
try {
return ActivityManager.getCurrentUser();
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 98764e02d803..f71f02a6ec4e 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -160,8 +160,6 @@ import com.android.server.storage.StorageSessionController.ExternalStorageServic
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal.ScreenObserver;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
import libcore.io.IoUtils;
import libcore.util.EmptyArray;
@@ -173,6 +171,8 @@ import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
import java.io.PrintWriter;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
@@ -3698,16 +3698,29 @@ class StorageManagerService extends IStorageManager.Stub
@Nullable
public PendingIntent getManageSpaceActivityIntent(
@NonNull String packageName, int requestCode) {
- // Only Apps with MANAGE_EXTERNAL_STORAGE permission should be able to call this API.
- enforcePermission(android.Manifest.permission.MANAGE_EXTERNAL_STORAGE);
-
- // We want to call the manageSpaceActivity as a SystemService and clear identity
- // of the calling App
+ // Only Apps with MANAGE_EXTERNAL_STORAGE permission which have package visibility for
+ // packageName should be able to call this API.
int originalUid = Binder.getCallingUidOrThrow();
- final long token = Binder.clearCallingIdentity();
+ try {
+ // Get package name for calling app and verify it has MANAGE_EXTERNAL_STORAGE permission
+ final String[] packagesFromUid = mIPackageManager.getPackagesForUid(originalUid);
+ if (packagesFromUid == null) {
+ throw new SecurityException("Unknown uid " + originalUid);
+ }
+ // Checking first entry in packagesFromUid is enough as using "sharedUserId"
+ // mechanism is rare and discouraged. Also, Apps that share same UID share the same
+ // permissions.
+ if (!mStorageManagerInternal.hasExternalStorageAccess(originalUid,
+ packagesFromUid[0])) {
+ throw new SecurityException("Only File Manager Apps permitted");
+ }
+ } catch (RemoteException re) {
+ throw new SecurityException("Unknown uid " + originalUid, re);
+ }
+ ApplicationInfo appInfo;
try {
- ApplicationInfo appInfo = mIPackageManager.getApplicationInfo(packageName, 0,
+ appInfo = mIPackageManager.getApplicationInfo(packageName, 0,
UserHandle.getUserId(originalUid));
if (appInfo == null) {
throw new IllegalArgumentException(
@@ -3717,8 +3730,15 @@ class StorageManagerService extends IStorageManager.Stub
Log.i(TAG, packageName + " doesn't have a manageSpaceActivity");
return null;
}
- Context targetAppContext = mContext.createPackageContext(packageName, 0);
+ } catch (RemoteException e) {
+ throw new SecurityException("Only File Manager Apps permitted");
+ }
+ // We want to call the manageSpaceActivity as a SystemService and clear identity
+ // of the calling App
+ final long token = Binder.clearCallingIdentity();
+ try {
+ Context targetAppContext = mContext.createPackageContext(packageName, 0);
Intent intent = new Intent(Intent.ACTION_DEFAULT);
intent.setClassName(packageName,
appInfo.manageSpaceActivityName);
@@ -3728,8 +3748,6 @@ class StorageManagerService extends IStorageManager.Stub
intent,
FLAG_ONE_SHOT | FLAG_CANCEL_CURRENT | FLAG_IMMUTABLE);
return activity;
- } catch (RemoteException e) {
- throw e.rethrowAsRuntimeException();
} catch (PackageManager.NameNotFoundException e) {
throw new IllegalArgumentException(
"packageName not found");
@@ -4955,19 +4973,17 @@ class StorageManagerService extends IStorageManager.Stub
@Override
public boolean hasExternalStorageAccess(int uid, String packageName) {
try {
- if (mIPackageManager.checkUidPermission(
- MANAGE_EXTERNAL_STORAGE, uid) == PERMISSION_GRANTED) {
- return true;
+ final int opMode = mIAppOpsService.checkOperation(
+ OP_MANAGE_EXTERNAL_STORAGE, uid, packageName);
+ if (opMode == AppOpsManager.MODE_DEFAULT) {
+ return mIPackageManager.checkUidPermission(
+ MANAGE_EXTERNAL_STORAGE, uid) == PERMISSION_GRANTED;
}
- if (mIAppOpsService.checkOperation(
- OP_MANAGE_EXTERNAL_STORAGE, uid, packageName) == MODE_ALLOWED) {
- return true;
- }
+ return opMode == AppOpsManager.MODE_ALLOWED;
} catch (RemoteException e) {
Slog.w("Failed to check MANAGE_EXTERNAL_STORAGE access for " + packageName, e);
}
-
return false;
}
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index 81627a05c9a4..c236a7f80000 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -19,6 +19,9 @@ package com.android.server;
import static android.app.UiModeManager.DEFAULT_PRIORITY;
import static android.app.UiModeManager.MODE_NIGHT_AUTO;
import static android.app.UiModeManager.MODE_NIGHT_CUSTOM;
+import static android.app.UiModeManager.MODE_NIGHT_CUSTOM_TYPE_BEDTIME;
+import static android.app.UiModeManager.MODE_NIGHT_CUSTOM_TYPE_SCHEDULE;
+import static android.app.UiModeManager.MODE_NIGHT_CUSTOM_TYPE_UNKNOWN;
import static android.app.UiModeManager.MODE_NIGHT_NO;
import static android.app.UiModeManager.MODE_NIGHT_YES;
import static android.app.UiModeManager.PROJECTION_TYPE_AUTOMOTIVE;
@@ -40,6 +43,7 @@ import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.StatusBarManager;
import android.app.UiModeManager;
+import android.app.UiModeManager.NightModeCustomType;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -115,6 +119,7 @@ final class UiModeManagerService extends SystemService {
private int mLastBroadcastState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
private int mNightMode = UiModeManager.MODE_NIGHT_NO;
+ private int mNightModeCustomType = UiModeManager.MODE_NIGHT_CUSTOM_TYPE_UNKNOWN;
private final LocalTime DEFAULT_CUSTOM_NIGHT_START_TIME = LocalTime.of(22, 0);
private final LocalTime DEFAULT_CUSTOM_NIGHT_END_TIME = LocalTime.of(6, 0);
private LocalTime mCustomAutoNightModeStartMilliseconds = DEFAULT_CUSTOM_NIGHT_START_TIME;
@@ -136,6 +141,7 @@ final class UiModeManagerService extends SystemService {
private boolean mWatch;
private boolean mVrHeadset;
private boolean mComputedNightMode;
+ private boolean mLastBedtimeRequestedNightMode = false;
private int mCarModeEnableFlags;
private boolean mSetupWizardComplete;
@@ -541,7 +547,9 @@ final class UiModeManagerService extends SystemService {
mNightMode = Secure.getIntForUser(context.getContentResolver(),
Secure.UI_NIGHT_MODE, res.getInteger(
com.android.internal.R.integer.config_defaultNightMode), userId);
- mOverrideNightModeOn = Secure.getIntForUser(context.getContentResolver(),
+ mNightModeCustomType = Secure.getIntForUser(context.getContentResolver(),
+ Secure.UI_NIGHT_MODE_CUSTOM_TYPE, MODE_NIGHT_CUSTOM_TYPE_UNKNOWN, userId);
+ mOverrideNightModeOn = Secure.getIntForUser(context.getContentResolver(),
Secure.UI_NIGHT_MODE_OVERRIDE_ON, 0, userId) != 0;
mOverrideNightModeOff = Secure.getIntForUser(context.getContentResolver(),
Secure.UI_NIGHT_MODE_OVERRIDE_OFF, 0, userId) != 0;
@@ -702,6 +710,14 @@ final class UiModeManagerService extends SystemService {
@Override
public void setNightMode(int mode) {
+ // MODE_NIGHT_CUSTOM_TYPE_SCHEDULE is the default for MODE_NIGHT_CUSTOM.
+ int customModeType = mode == MODE_NIGHT_CUSTOM
+ ? MODE_NIGHT_CUSTOM_TYPE_SCHEDULE
+ : MODE_NIGHT_CUSTOM_TYPE_UNKNOWN;
+ setNightModeInternal(mode, customModeType);
+ }
+
+ private void setNightModeInternal(int mode, int customModeType) {
if (isNightModeLocked() && (getContext().checkCallingOrSelfPermission(
android.Manifest.permission.MODIFY_DAY_NIGHT_MODE)
!= PackageManager.PERMISSION_GRANTED)) {
@@ -722,12 +738,14 @@ final class UiModeManagerService extends SystemService {
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
- if (mNightMode != mode) {
+ if (mNightMode != mode || mNightModeCustomType != customModeType) {
if (mNightMode == MODE_NIGHT_AUTO || mNightMode == MODE_NIGHT_CUSTOM) {
unregisterScreenOffEventLocked();
cancelCustomAlarm();
}
-
+ mNightModeCustomType = mode == MODE_NIGHT_CUSTOM
+ ? customModeType
+ : MODE_NIGHT_CUSTOM_TYPE_UNKNOWN;
mNightMode = mode;
resetNightModeOverrideLocked();
persistNightMode(user);
@@ -754,6 +772,30 @@ final class UiModeManagerService extends SystemService {
}
@Override
+ public void setNightModeCustomType(@NightModeCustomType int nightModeCustomType) {
+ if (getContext().checkCallingOrSelfPermission(
+ android.Manifest.permission.MODIFY_DAY_NIGHT_MODE)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException(
+ "setNightModeCustomType requires MODIFY_DAY_NIGHT_MODE permission");
+ }
+ setNightModeInternal(MODE_NIGHT_CUSTOM, nightModeCustomType);
+ }
+
+ @Override
+ public int getNightModeCustomType() {
+ if (getContext().checkCallingOrSelfPermission(
+ android.Manifest.permission.MODIFY_DAY_NIGHT_MODE)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException(
+ "getNightModeCustomType requires MODIFY_DAY_NIGHT_MODE permission");
+ }
+ synchronized (mLock) {
+ return mNightModeCustomType;
+ }
+ }
+
+ @Override
public void setApplicationNightMode(@UiModeManager.NightMode int mode) {
switch (mode) {
case UiModeManager.MODE_NIGHT_NO:
@@ -808,10 +850,19 @@ final class UiModeManagerService extends SystemService {
}
@Override
+ public boolean setNightModeActivatedForCustomMode(int modeNightCustomType, boolean active) {
+ return setNightModeActivatedForModeInternal(modeNightCustomType, active);
+ }
+
+ @Override
public boolean setNightModeActivated(boolean active) {
- if (isNightModeLocked() && (getContext().checkCallingOrSelfPermission(
+ return setNightModeActivatedForModeInternal(mNightModeCustomType, active);
+ }
+
+ private boolean setNightModeActivatedForModeInternal(int modeCustomType, boolean active) {
+ if (getContext().checkCallingOrSelfPermission(
android.Manifest.permission.MODIFY_DAY_NIGHT_MODE)
- != PackageManager.PERMISSION_GRANTED)) {
+ != PackageManager.PERMISSION_GRANTED) {
Slog.e(TAG, "Night mode locked, requires MODIFY_DAY_NIGHT_MODE permission");
return false;
}
@@ -824,6 +875,14 @@ final class UiModeManagerService extends SystemService {
return false;
}
+ // Store the last requested bedtime night mode state so that we don't need to notify
+ // anyone if the user decides to switch to the night mode to bedtime.
+ if (modeCustomType == MODE_NIGHT_CUSTOM_TYPE_BEDTIME) {
+ mLastBedtimeRequestedNightMode = active;
+ }
+ if (modeCustomType != mNightModeCustomType) {
+ return false;
+ }
synchronized (mLock) {
final long ident = Binder.clearCallingIdentity();
try {
@@ -1422,6 +1481,8 @@ final class UiModeManagerService extends SystemService {
Secure.putIntForUser(getContext().getContentResolver(),
Secure.UI_NIGHT_MODE, mNightMode, user);
Secure.putLongForUser(getContext().getContentResolver(),
+ Secure.UI_NIGHT_MODE_CUSTOM_TYPE, mNightModeCustomType, user);
+ Secure.putLongForUser(getContext().getContentResolver(),
Secure.DARK_THEME_CUSTOM_START_TIME,
mCustomAutoNightModeStartMilliseconds.toNanoOfDay() / 1000, user);
Secure.putLongForUser(getContext().getContentResolver(),
@@ -1473,10 +1534,14 @@ final class UiModeManagerService extends SystemService {
}
if (mNightMode == MODE_NIGHT_CUSTOM) {
- registerTimeChangeEvent();
- final boolean activate = computeCustomNightMode();
- updateComputedNightModeLocked(activate);
- scheduleNextCustomTimeListener();
+ if (mNightModeCustomType == MODE_NIGHT_CUSTOM_TYPE_BEDTIME) {
+ updateComputedNightModeLocked(mLastBedtimeRequestedNightMode);
+ } else {
+ registerTimeChangeEvent();
+ final boolean activate = computeCustomNightMode();
+ updateComputedNightModeLocked(activate);
+ scheduleNextCustomTimeListener();
+ }
} else {
unregisterTimeChangeEvent();
}
@@ -1494,6 +1559,7 @@ final class UiModeManagerService extends SystemService {
"updateConfigurationLocked: mDockState=" + mDockState
+ "; mCarMode=" + mCarModeEnabled
+ "; mNightMode=" + mNightMode
+ + "; mNightModeCustomType=" + mNightModeCustomType
+ "; uiMode=" + uiMode);
}
@@ -1534,7 +1600,8 @@ final class UiModeManagerService extends SystemService {
}
private boolean shouldApplyAutomaticChangesImmediately() {
- return mCar || !mPowerManager.isInteractive();
+ return mCar || !mPowerManager.isInteractive()
+ || mNightModeCustomType == MODE_NIGHT_CUSTOM_TYPE_BEDTIME;
}
private void scheduleNextCustomTimeListener() {
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index b92556bf9a20..888710857706 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -4138,7 +4138,7 @@ public final class ActiveServices {
// for a previous process to come up. To deal with this, we store
// in the service any current isolated process it is running in or
// waiting to have come up.
- app = r.isolatedProc;
+ app = r.isolationHostProc;
if (WebViewZygote.isMultiprocessEnabled()
&& r.serviceInfo.packageName.equals(WebViewZygote.getPackageName())) {
hostingRecord = HostingRecord.byWebviewZygote(r.instanceName);
@@ -4165,7 +4165,7 @@ public final class ActiveServices {
return msg;
}
if (isolated) {
- r.isolatedProc = app;
+ r.isolationHostProc = app;
}
}
@@ -4976,7 +4976,7 @@ public final class ActiveServices {
try {
for (int i=0; i<mPendingServices.size(); i++) {
sr = mPendingServices.get(i);
- if (proc != sr.isolatedProc && (proc.uid != sr.appInfo.uid
+ if (proc != sr.isolationHostProc && (proc.uid != sr.appInfo.uid
|| !processName.equals(sr.processName))) {
continue;
}
@@ -5016,7 +5016,7 @@ public final class ActiveServices {
boolean didImmediateRestart = false;
for (int i=0; i<mRestartingServices.size(); i++) {
sr = mRestartingServices.get(i);
- if (proc != sr.isolatedProc && (proc.uid != sr.appInfo.uid
+ if (proc != sr.isolationHostProc && (proc.uid != sr.appInfo.uid
|| !processName.equals(sr.processName))) {
continue;
}
@@ -5048,9 +5048,9 @@ public final class ActiveServices {
ServiceRecord sr = mPendingServices.get(i);
if ((proc.uid == sr.appInfo.uid
&& proc.processName.equals(sr.processName))
- || sr.isolatedProc == proc) {
+ || sr.isolationHostProc == proc) {
Slog.w(TAG, "Forcing bringing down service: " + sr);
- sr.isolatedProc = null;
+ sr.isolationHostProc = null;
mPendingServices.remove(i);
size = mPendingServices.size();
i--;
@@ -5083,7 +5083,7 @@ public final class ActiveServices {
stopServiceAndUpdateAllowlistManagerLocked(service);
}
service.setProcess(null, null, 0, null);
- service.isolatedProc = null;
+ service.isolationHostProc = null;
if (mTmpCollectionResults == null) {
mTmpCollectionResults = new ArrayList<>();
}
@@ -5321,7 +5321,7 @@ public final class ActiveServices {
sr.app.mServices.updateBoundClientUids();
}
sr.setProcess(null, null, 0, null);
- sr.isolatedProc = null;
+ sr.isolationHostProc = null;
sr.executeNesting = 0;
synchronized (mAm.mProcessStats.mLock) {
sr.forceClearTracker();
@@ -6792,7 +6792,8 @@ public final class ActiveServices {
r.mFgsNotificationShown,
durationMs,
r.mStartForegroundCount,
- ActivityManagerUtils.hashComponentNameForAtom(r.shortInstanceName));
+ ActivityManagerUtils.hashComponentNameForAtom(r.shortInstanceName),
+ r.mFgsHasNotificationPermission);
}
boolean canAllowWhileInUsePermissionInFgsLocked(int callingPid, int callingUid,
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index f67e732b47dd..b1b4c4447ec8 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2871,13 +2871,51 @@ public class ActivityManagerService extends IActivityManager.Stub
return mode == AppOpsManager.MODE_ALLOWED;
}
- @Override
- public int getPackageProcessState(String packageName, String callingPackage) {
+ /**
+ * Checks whether the calling package is trusted.
+ *
+ * The calling package is trusted if it's from system or the supposed package name matches the
+ * UID making the call.
+ *
+ * @throws SecurityException if the package name and UID don't match.
+ */
+ private void verifyCallingPackage(String callingPackage) {
+ final int callingUid = Binder.getCallingUid();
+ // The caller is System or Shell.
+ if (callingUid == SYSTEM_UID || isCallerShell()) {
+ return;
+ }
+
+ // Handle the special UIDs that don't have real package (audioserver, cameraserver, etc).
+ final String resolvedPackage = AppOpsManager.resolvePackageName(callingUid,
+ null /* packageName */);
+ if (resolvedPackage != null && resolvedPackage.equals(callingPackage)) {
+ return;
+ }
+
+ final int claimedUid = getPackageManagerInternal().getPackageUid(callingPackage,
+ 0 /* flags */, UserHandle.getUserId(callingUid));
+ if (callingUid == claimedUid) {
+ return;
+ }
+
+ throw new SecurityException(
+ "Claimed calling package " + callingPackage + " does not match the calling UID "
+ + Binder.getCallingUid());
+ }
+
+ private void enforceUsageStatsPermission(String callingPackage, String func) {
+ verifyCallingPackage(callingPackage);
+ // Since the protection level of PACKAGE_USAGE_STATS has 'appop', apps may grant this
+ // permission via that way. We need to check both app-ops and permission.
if (!hasUsageStatsPermission(callingPackage)) {
- enforceCallingPermission(android.Manifest.permission.PACKAGE_USAGE_STATS,
- "getPackageProcessState");
+ enforceCallingPermission(android.Manifest.permission.PACKAGE_USAGE_STATS, func);
}
+ }
+ @Override
+ public int getPackageProcessState(String packageName, String callingPackage) {
+ enforceUsageStatsPermission(callingPackage, "getPackageProcessState");
final int[] procState = {PROCESS_STATE_NONEXISTENT};
synchronized (mProcLock) {
mProcessList.forEachLruProcessesLOSP(false, proc -> {
@@ -6938,11 +6976,7 @@ public class ActivityManagerService extends IActivityManager.Stub
@Override
public int getUidProcessState(int uid, String callingPackage) {
- if (!hasUsageStatsPermission(callingPackage)) {
- enforceCallingPermission(android.Manifest.permission.PACKAGE_USAGE_STATS,
- "getUidProcessState");
- }
-
+ enforceUsageStatsPermission(callingPackage, "getUidProcessState");
synchronized (mProcLock) {
return mProcessList.getUidProcStateLOSP(uid);
}
@@ -6950,11 +6984,7 @@ public class ActivityManagerService extends IActivityManager.Stub
@Override
public @ProcessCapability int getUidProcessCapabilities(int uid, String callingPackage) {
- if (!hasUsageStatsPermission(callingPackage)) {
- enforceCallingPermission(android.Manifest.permission.PACKAGE_USAGE_STATS,
- "getUidProcessState");
- }
-
+ enforceUsageStatsPermission(callingPackage, "getUidProcessCapabilities");
synchronized (mProcLock) {
return mProcessList.getUidProcessCapabilityLOSP(uid);
}
@@ -6963,10 +6993,7 @@ public class ActivityManagerService extends IActivityManager.Stub
@Override
public void registerUidObserver(IUidObserver observer, int which, int cutpoint,
String callingPackage) {
- if (!hasUsageStatsPermission(callingPackage)) {
- enforceCallingPermission(android.Manifest.permission.PACKAGE_USAGE_STATS,
- "registerUidObserver");
- }
+ enforceUsageStatsPermission(callingPackage, "registerUidObserver");
mUidObserverController.register(observer, which, cutpoint, callingPackage,
Binder.getCallingUid());
}
@@ -6978,10 +7005,7 @@ public class ActivityManagerService extends IActivityManager.Stub
@Override
public boolean isUidActive(int uid, String callingPackage) {
- if (!hasUsageStatsPermission(callingPackage)) {
- enforceCallingPermission(android.Manifest.permission.PACKAGE_USAGE_STATS,
- "isUidActive");
- }
+ enforceUsageStatsPermission(callingPackage, "isUidActive");
synchronized (mProcLock) {
if (isUidActiveLOSP(uid)) {
return true;
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 9ffafe256033..0b92954e7932 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -42,6 +42,7 @@ import android.os.BatteryStatsInternal;
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
import android.os.Binder;
+import android.os.BluetoothBatteryStats;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
@@ -2233,6 +2234,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
pw.println(" --read-daily: read-load last written daily stats.");
pw.println(" --settings: dump the settings key/values related to batterystats");
pw.println(" --cpu: dump cpu stats for debugging purpose");
+ pw.println(" --power-profile: dump the power profile constants");
pw.println(" <package.name>: optional name of package to filter output by.");
pw.println(" -h: print this help text.");
pw.println("Battery stats (batterystats) commands:");
@@ -2270,6 +2272,12 @@ public final class BatteryStatsService extends IBatteryStats.Stub
}
}
+ private void dumpPowerProfile(PrintWriter pw) {
+ synchronized (mStats) {
+ mStats.dumpPowerProfileLocked(pw);
+ }
+ }
+
private int doEnableOrDisable(PrintWriter pw, int i, String[] args, boolean enable) {
i++;
if (i >= args.length) {
@@ -2412,6 +2420,9 @@ public final class BatteryStatsService extends IBatteryStats.Stub
} else if ("--measured-energy".equals(arg)) {
dumpMeasuredEnergyStats(pw);
return;
+ } else if ("--power-profile".equals(arg)) {
+ dumpPowerProfile(pw);
+ return;
} else if ("-a".equals(arg)) {
flags |= BatteryStats.DUMP_VERBOSE;
} else if (arg.length() > 0 && arg.charAt(0) == '-'){
@@ -2617,6 +2628,20 @@ public final class BatteryStatsService extends IBatteryStats.Stub
}
/**
+ * Gets a snapshot of Bluetooth stats
+ * @hide
+ */
+ public BluetoothBatteryStats getBluetoothBatteryStats() {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BATTERY_STATS, null);
+
+ // Wait for the completion of pending works if there is any
+ awaitCompletion();
+ synchronized (mStats) {
+ return mStats.getBluetoothBatteryStats();
+ }
+ }
+
+ /**
* Gets a snapshot of the system health for a particular uid.
*/
@Override
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index eb8a4e9508da..e36ea20dea48 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -781,6 +781,54 @@ public final class BroadcastQueue {
skip = true;
}
}
+ // Check that the receiver does *not* have any excluded permissions
+ if (!skip && r.excludedPermissions != null && r.excludedPermissions.length > 0) {
+ for (int i = 0; i < r.excludedPermissions.length; i++) {
+ String excludedPermission = r.excludedPermissions[i];
+ final int perm = mService.checkComponentPermission(excludedPermission,
+ filter.receiverList.pid, filter.receiverList.uid, -1, true);
+
+ int appOp = AppOpsManager.permissionToOpCode(excludedPermission);
+ if (appOp != AppOpsManager.OP_NONE) {
+ // When there is an app op associated with the permission,
+ // skip when both the permission and the app op are
+ // granted.
+ if ((perm == PackageManager.PERMISSION_GRANTED) && (
+ mService.getAppOpsManager().checkOpNoThrow(appOp,
+ filter.receiverList.uid,
+ filter.packageName)
+ == AppOpsManager.MODE_ALLOWED)) {
+ Slog.w(TAG, "Appop Denial: receiving "
+ + r.intent.toString()
+ + " to " + filter.receiverList.app
+ + " (pid=" + filter.receiverList.pid
+ + ", uid=" + filter.receiverList.uid + ")"
+ + " excludes appop " + AppOpsManager.permissionToOp(
+ excludedPermission)
+ + " due to sender " + r.callerPackage
+ + " (uid " + r.callingUid + ")");
+ skip = true;
+ break;
+ }
+ } else {
+ // When there is no app op associated with the permission,
+ // skip when permission is granted.
+ if (perm == PackageManager.PERMISSION_GRANTED) {
+ Slog.w(TAG, "Permission Denial: receiving "
+ + r.intent.toString()
+ + " to " + filter.receiverList.app
+ + " (pid=" + filter.receiverList.pid
+ + ", uid=" + filter.receiverList.uid + ")"
+ + " excludes " + excludedPermission
+ + " due to sender " + r.callerPackage
+ + " (uid " + r.callingUid + ")");
+ skip = true;
+ break;
+ }
+ }
+ }
+ }
+
// If the broadcast also requires an app op check that as well.
if (!skip && r.appOp != AppOpsManager.OP_NONE
&& mService.getAppOpsManager().noteOpNoThrow(r.appOp,
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index c08cf6485855..6f22c61d2b2a 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -962,7 +962,7 @@ public final class CachedAppOptimizer {
}
opt.setFreezerOverride(false);
- if (!opt.isFrozen()) {
+ if (pid == 0 || !opt.isFrozen()) {
return;
}
diff --git a/services/core/java/com/android/server/am/ContentProviderHelper.java b/services/core/java/com/android/server/am/ContentProviderHelper.java
index 625dd636b24e..3c5b872ec717 100644
--- a/services/core/java/com/android/server/am/ContentProviderHelper.java
+++ b/services/core/java/com/android/server/am/ContentProviderHelper.java
@@ -15,6 +15,7 @@
*/
package com.android.server.am;
+import static android.content.ContentProvider.isAuthorityRedirectedForCloneProfile;
import static android.os.Process.PROC_CHAR;
import static android.os.Process.PROC_OUT_LONG;
import static android.os.Process.PROC_PARENS;
@@ -46,6 +47,7 @@ import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.PathPermission;
import android.content.pm.ProviderInfo;
+import android.content.pm.UserInfo;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Binder;
@@ -72,6 +74,7 @@ import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ArrayUtils;
import com.android.server.LocalServices;
import com.android.server.RescueParty;
+import com.android.server.pm.UserManagerInternal;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import java.io.FileDescriptor;
@@ -177,12 +180,22 @@ public class ContentProviderHelper {
cpr = mProviderMap.getProviderByName(name, UserHandle.USER_SYSTEM);
if (cpr != null) {
cpi = cpr.info;
+
if (mService.isSingleton(
cpi.processName, cpi.applicationInfo, cpi.name, cpi.flags)
&& mService.isValidSingletonCall(
r == null ? callingUid : r.uid, cpi.applicationInfo.uid)) {
userId = UserHandle.USER_SYSTEM;
checkCrossUser = false;
+ } else if (isAuthorityRedirectedForCloneProfile(name)) {
+ UserManagerInternal umInternal = LocalServices.getService(
+ UserManagerInternal.class);
+ UserInfo userInfo = umInternal.getUserInfo(userId);
+
+ if (userInfo != null && userInfo.isCloneProfile()) {
+ userId = umInternal.getProfileParentId(userId);
+ checkCrossUser = false;
+ }
} else {
cpr = null;
cpi = null;
@@ -1026,6 +1039,7 @@ public class ContentProviderHelper {
* at the given authority and user.
*/
String checkContentProviderAccess(String authority, int userId) {
+ boolean checkUser = true;
if (userId == UserHandle.USER_ALL) {
mService.mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, TAG);
@@ -1041,6 +1055,17 @@ public class ContentProviderHelper {
| PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
userId);
+ if (cpi == null && isAuthorityRedirectedForCloneProfile(authority)) {
+ // This might be a provider that's running only in the system user that's
+ // also serving clone profiles
+ cpi = AppGlobals.getPackageManager().resolveContentProvider(authority,
+ ActivityManagerService.STOCK_PM_FLAGS
+ | PackageManager.GET_URI_PERMISSION_PATTERNS
+ | PackageManager.MATCH_DISABLED_COMPONENTS
+ | PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
+ UserHandle.USER_SYSTEM);
+ }
} catch (RemoteException ignored) {
}
if (cpi == null) {
@@ -1048,6 +1073,16 @@ public class ContentProviderHelper {
+ "; expected to find a valid ContentProvider for this authority";
}
+ if (isAuthorityRedirectedForCloneProfile(authority)) {
+ UserManagerInternal umInternal = LocalServices.getService(UserManagerInternal.class);
+ UserInfo userInfo = umInternal.getUserInfo(userId);
+
+ if (userInfo != null && userInfo.isCloneProfile()) {
+ userId = umInternal.getProfileParentId(userId);
+ checkUser = false;
+ }
+ }
+
final int callingPid = Binder.getCallingPid();
ProcessRecord r;
final String appName;
@@ -1060,7 +1095,7 @@ public class ContentProviderHelper {
}
return checkContentProviderPermission(cpi, callingPid, Binder.getCallingUid(),
- userId, true, appName);
+ userId, checkUser, appName);
}
int checkContentProviderUriPermission(Uri uri, int userId, int callingUid, int modeFlags) {
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index b1234962efc2..bdfd02e9c25a 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -598,7 +598,7 @@ public class OomAdjuster {
for (int i = psr.numberOfConnections() - 1; i >= 0; i--) {
ConnectionRecord cr = psr.getConnectionAt(i);
ProcessRecord service = (cr.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) != 0
- ? cr.binding.service.isolatedProc : cr.binding.service.app;
+ ? cr.binding.service.isolationHostProc : cr.binding.service.app;
if (service == null || service == pr) {
continue;
}
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index c830554c398b..be187e21db47 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -513,7 +513,7 @@ class ProcessRecord implements WindowProcessListener {
}
}
processInfo = procInfo;
- isolated = _info.uid != _uid;
+ isolated = Process.isIsolated(_uid);
appZygote = (UserHandle.getAppId(_uid) >= Process.FIRST_APP_ZYGOTE_ISOLATED_UID
&& UserHandle.getAppId(_uid) <= Process.LAST_APP_ZYGOTE_ISOLATED_UID);
uid = _uid;
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index b3e46cd0b526..24e7ba4a32b9 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -102,7 +102,8 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
// IBinder -> ConnectionRecord of all bound clients
ProcessRecord app; // where this service is running or null.
- ProcessRecord isolatedProc; // keep track of isolated process, if requested
+ ProcessRecord isolationHostProc; // process which we've started for this service (used for
+ // isolated and supplemental processes)
ServiceState tracker; // tracking service execution, may be null
ServiceState restartTracker; // tracking service restart
boolean allowlistManager; // any bindings to this service have BIND_ALLOW_WHITELIST_MANAGEMENT?
@@ -175,7 +176,6 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
// FGS notification was shown before the FGS finishes, or it wasn't deferred in the first place.
boolean mFgsNotificationShown;
// Whether FGS package has permissions to show notifications.
- // TODO(b/194833441): Output this field to logs in ActiveServices#logFGSStateChangeLocked.
boolean mFgsHasNotificationPermission;
// allow the service becomes foreground service? Service started from background may not be
@@ -353,8 +353,8 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
if (app != null) {
app.dumpDebug(proto, ServiceRecordProto.APP);
}
- if (isolatedProc != null) {
- isolatedProc.dumpDebug(proto, ServiceRecordProto.ISOLATED_PROC);
+ if (isolationHostProc != null) {
+ isolationHostProc.dumpDebug(proto, ServiceRecordProto.ISOLATED_PROC);
}
proto.write(ServiceRecordProto.WHITELIST_MANAGER, allowlistManager);
proto.write(ServiceRecordProto.DELAYED, delayed);
@@ -456,8 +456,8 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
pw.print(prefix); pw.print("dataDir="); pw.println(appInfo.dataDir);
}
pw.print(prefix); pw.print("app="); pw.println(app);
- if (isolatedProc != null) {
- pw.print(prefix); pw.print("isolatedProc="); pw.println(isolatedProc);
+ if (isolationHostProc != null) {
+ pw.print(prefix); pw.print("isolationHostProc="); pw.println(isolationHostProc);
}
if (allowlistManager) {
pw.print(prefix); pw.print("allowlistManager="); pw.println(allowlistManager);
@@ -593,6 +593,10 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
userId = UserHandle.getUserId(appInfo.uid);
createdFromFg = callerIsFg;
updateKeepWarmLocked();
+ // initialize notification permission state; this'll be updated whenever there's an attempt
+ // to post or update a notification, but that doesn't cover the time before the first
+ // notification
+ updateFgsHasNotificationPermission();
}
public ServiceState getTracker() {
@@ -950,6 +954,25 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
return lastStartId;
}
+ private void updateFgsHasNotificationPermission() {
+ // Do asynchronous communication with notification manager to avoid deadlocks.
+ final String localPackageName = packageName;
+ final int appUid = appInfo.uid;
+
+ ams.mHandler.post(new Runnable() {
+ public void run() {
+ NotificationManagerInternal nm = LocalServices.getService(
+ NotificationManagerInternal.class);
+ if (nm == null) {
+ return;
+ }
+ // Record whether the package has permission to notify the user
+ mFgsHasNotificationPermission = nm.areNotificationsEnabledForPackage(
+ localPackageName, appUid);
+ }
+ });
+ }
+
public void postNotification() {
if (isForeground && foregroundNoti != null && app != null) {
final int appUid = appInfo.uid;
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 028a0ec8648a..252584c76696 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -2903,7 +2903,7 @@ class UserController implements Handler.Callback {
*/
mHandler.removeMessages(CLEAR_USER_JOURNEY_SESSION_MSG);
mHandler.sendMessageDelayed(mHandler.obtainMessage(CLEAR_USER_JOURNEY_SESSION_MSG,
- target.id), USER_JOURNEY_TIMEOUT_MS);
+ target.id, /* arg2= */ 0), USER_JOURNEY_TIMEOUT_MS);
}
FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED, newSessionId,
diff --git a/services/core/java/com/android/server/ambientcontext/AmbientContextManagerPerUserService.java b/services/core/java/com/android/server/ambientcontext/AmbientContextManagerPerUserService.java
new file mode 100644
index 000000000000..6982513f8f2f
--- /dev/null
+++ b/services/core/java/com/android/server/ambientcontext/AmbientContextManagerPerUserService.java
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.ambientcontext;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.AppGlobals;
+import android.app.BroadcastOptions;
+import android.app.PendingIntent;
+import android.app.ambientcontext.AmbientContextEvent;
+import android.app.ambientcontext.AmbientContextEventRequest;
+import android.app.ambientcontext.AmbientContextEventResponse;
+import android.app.ambientcontext.AmbientContextManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
+import android.os.Binder;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+import android.service.ambientcontext.AmbientContextDetectionService;
+import android.util.IndentingPrintWriter;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.infra.AbstractPerUserSystemService;
+
+import java.io.PrintWriter;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Per-user manager service for {@link AmbientContextEvent}s.
+ */
+final class AmbientContextManagerPerUserService extends
+ AbstractPerUserSystemService<AmbientContextManagerPerUserService,
+ AmbientContextManagerService> {
+ private static final String TAG = AmbientContextManagerPerUserService.class.getSimpleName();
+
+ @Nullable
+ @VisibleForTesting
+ RemoteAmbientContextDetectionService mRemoteService;
+
+ private ComponentName mComponentName;
+ private Context mContext;
+ private Set<PendingIntent> mExistingPendingIntents;
+
+ AmbientContextManagerPerUserService(
+ @NonNull AmbientContextManagerService master, Object lock, @UserIdInt int userId) {
+ super(master, lock, userId);
+ mContext = master.getContext();
+ mExistingPendingIntents = new HashSet<>();
+ }
+
+ void destroyLocked() {
+ if (isVerbose()) {
+ Slog.v(TAG, "destroyLocked()");
+ }
+
+ Slog.d(TAG, "Trying to cancel the remote request. Reason: Service destroyed.");
+ if (mRemoteService != null) {
+ synchronized (mLock) {
+ mRemoteService.unbind();
+ mRemoteService = null;
+ }
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void ensureRemoteServiceInitiated() {
+ if (mRemoteService == null) {
+ mRemoteService = new RemoteAmbientContextDetectionService(
+ getContext(), mComponentName, getUserId());
+ }
+ }
+
+ /**
+ * get the currently bound component name.
+ */
+ @VisibleForTesting
+ ComponentName getComponentName() {
+ return mComponentName;
+ }
+
+
+ /**
+ * Resolves and sets up the service if it had not been done yet. Returns true if the service
+ * is available.
+ */
+ @GuardedBy("mLock")
+ @VisibleForTesting
+ boolean setUpServiceIfNeeded() {
+ if (mComponentName == null) {
+ mComponentName = updateServiceInfoLocked();
+ }
+ return mComponentName != null;
+ }
+
+ @Override
+ protected ServiceInfo newServiceInfoLocked(@NonNull ComponentName serviceComponent)
+ throws PackageManager.NameNotFoundException {
+ ServiceInfo serviceInfo;
+ try {
+ serviceInfo = AppGlobals.getPackageManager().getServiceInfo(serviceComponent,
+ 0, mUserId);
+ if (serviceInfo != null) {
+ final String permission = serviceInfo.permission;
+ if (!Manifest.permission.BIND_AMBIENT_CONTEXT_DETECTION_SERVICE.equals(
+ permission)) {
+ throw new SecurityException(String.format(
+ "Service %s requires %s permission. Found %s permission",
+ serviceInfo.getComponentName(),
+ Manifest.permission.BIND_AMBIENT_CONTEXT_DETECTION_SERVICE,
+ serviceInfo.permission));
+ }
+ }
+ } catch (RemoteException e) {
+ throw new PackageManager.NameNotFoundException(
+ "Could not get service for " + serviceComponent);
+ }
+ return serviceInfo;
+ }
+
+ @Override
+ protected void dumpLocked(@NonNull String prefix, @NonNull PrintWriter pw) {
+ synchronized (super.mLock) {
+ super.dumpLocked(prefix, pw);
+ }
+ if (mRemoteService != null) {
+ mRemoteService.dump("", new IndentingPrintWriter(pw, " "));
+ }
+ }
+
+ /**
+ * Handles client registering as an observer. Only one registration is supported per app
+ * package. A new registration from the same package will overwrite the previous registration.
+ */
+ public void onRegisterObserver(AmbientContextEventRequest request,
+ PendingIntent pendingIntent) {
+ synchronized (mLock) {
+ if (!setUpServiceIfNeeded()) {
+ Slog.w(TAG, "Service is not available at this moment.");
+ sendStatusUpdateIntent(
+ pendingIntent, AmbientContextEventResponse.STATUS_SERVICE_UNAVAILABLE);
+ return;
+ }
+
+ // Remove any existing intent and unregister for this package before adding a new one.
+ String callingPackage = pendingIntent.getCreatorPackage();
+ PendingIntent duplicatePendingIntent = findExistingRequestByPackage(callingPackage);
+ if (duplicatePendingIntent != null) {
+ Slog.d(TAG, "Unregister duplicate request from " + callingPackage);
+ onUnregisterObserver(callingPackage);
+ mExistingPendingIntents.remove(duplicatePendingIntent);
+ }
+
+ // Register new package and add request to mExistingRequests
+ startDetection(request, callingPackage, createRemoteCallback());
+ mExistingPendingIntents.add(pendingIntent);
+ }
+ }
+
+ @VisibleForTesting
+ void startDetection(AmbientContextEventRequest request, String callingPackage,
+ RemoteCallback callback) {
+ Slog.d(TAG, "Requested detection of " + request.getEventTypes());
+ synchronized (mLock) {
+ ensureRemoteServiceInitiated();
+ mRemoteService.startDetection(request, callingPackage, callback);
+ }
+ }
+
+ /**
+ * Sends an intent with a status code and empty events.
+ */
+ void sendStatusUpdateIntent(PendingIntent pendingIntent, int statusCode) {
+ AmbientContextEventResponse response = new AmbientContextEventResponse.Builder()
+ .setStatusCode(statusCode)
+ .build();
+ sendResponseIntent(pendingIntent, response);
+ }
+
+ /**
+ * Unregisters the client from all previously registered events by removing from the
+ * mExistingRequests map, and unregister events from the service if those events are not
+ * requested by other apps.
+ */
+ public void onUnregisterObserver(String callingPackage) {
+ synchronized (mLock) {
+ PendingIntent pendingIntent = findExistingRequestByPackage(callingPackage);
+ if (pendingIntent == null) {
+ Slog.d(TAG, "No registration found for " + callingPackage);
+ return;
+ }
+
+ // Remove from existing requests
+ mExistingPendingIntents.remove(pendingIntent);
+ stopDetection(pendingIntent.getCreatorPackage());
+ }
+ }
+
+ @VisibleForTesting
+ void stopDetection(String packageName) {
+ Slog.d(TAG, "Stop detection for " + packageName);
+ synchronized (mLock) {
+ ensureRemoteServiceInitiated();
+ mRemoteService.stopDetection(packageName);
+ }
+ }
+
+ @Nullable
+ private PendingIntent findExistingRequestByPackage(String callingPackage) {
+ for (PendingIntent pendingIntent : mExistingPendingIntents) {
+ if (pendingIntent.getCreatorPackage().equals(callingPackage)) {
+ return pendingIntent;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Sends out the Intent to the client after the event is detected.
+ *
+ * @param pendingIntent Client's PendingIntent for callback
+ * @param response Response with status code and detection result
+ */
+ private void sendResponseIntent(PendingIntent pendingIntent,
+ AmbientContextEventResponse response) {
+ Intent intent = new Intent();
+ intent.putExtra(AmbientContextManager.EXTRA_AMBIENT_CONTEXT_EVENT_RESPONSE, response);
+ // Explicitly disallow the receiver from starting activities, to prevent apps from utilizing
+ // the PendingIntent as a backdoor to do this.
+ BroadcastOptions options = BroadcastOptions.makeBasic();
+ options.setPendingIntentBackgroundActivityLaunchAllowed(false);
+ try {
+ pendingIntent.send(getContext(), 0, intent, null, null, null,
+ options.toBundle());
+ Slog.i(TAG, "Sending PendingIntent to " + pendingIntent.getCreatorPackage() + ": "
+ + response);
+ } catch (PendingIntent.CanceledException e) {
+ Slog.w(TAG, "Couldn't deliver pendingIntent:" + pendingIntent);
+ }
+ }
+
+ @NonNull
+ private RemoteCallback createRemoteCallback() {
+ return new RemoteCallback(result -> {
+ AmbientContextEventResponse response = (AmbientContextEventResponse) result.get(
+ AmbientContextDetectionService.RESPONSE_BUNDLE_KEY);
+ final long token = Binder.clearCallingIdentity();
+ try {
+ Set<PendingIntent> pendingIntentForFailedRequests = new HashSet<>();
+ for (PendingIntent pendingIntent : mExistingPendingIntents) {
+ // Send PendingIntent if a requesting package matches the response packages.
+ if (response.getPackageName().equals(pendingIntent.getCreatorPackage())) {
+ sendResponseIntent(pendingIntent, response);
+
+ int statusCode = response.getStatusCode();
+ if (statusCode != AmbientContextEventResponse.STATUS_SUCCESS) {
+ pendingIntentForFailedRequests.add(pendingIntent);
+ }
+ Slog.i(TAG, "Got response of " + response.getEvents() + " for "
+ + pendingIntent.getCreatorPackage() + ". Status: " + statusCode);
+ }
+ }
+
+ // Removes the failed requests from the existing requests.
+ for (PendingIntent pendingIntent : pendingIntentForFailedRequests) {
+ mExistingPendingIntents.remove(pendingIntent);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ });
+ }
+}
diff --git a/services/core/java/com/android/server/ambientcontext/AmbientContextManagerService.java b/services/core/java/com/android/server/ambientcontext/AmbientContextManagerService.java
new file mode 100644
index 000000000000..33905f2d1aa3
--- /dev/null
+++ b/services/core/java/com/android/server/ambientcontext/AmbientContextManagerService.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.ambientcontext;
+
+import static android.provider.DeviceConfig.NAMESPACE_AMBIENT_CONTEXT_MANAGER_SERVICE;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.app.PendingIntent;
+import android.app.ambientcontext.AmbientContextEvent;
+import android.app.ambientcontext.AmbientContextEventRequest;
+import android.app.ambientcontext.AmbientContextEventResponse;
+import android.app.ambientcontext.IAmbientContextEventObserver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManagerInternal;
+import android.os.RemoteCallback;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
+import android.os.UserHandle;
+import android.provider.DeviceConfig;
+import android.util.Slog;
+
+import com.android.internal.R;
+import com.android.internal.util.DumpUtils;
+import com.android.server.LocalServices;
+import com.android.server.SystemService;
+import com.android.server.infra.AbstractMasterSystemService;
+import com.android.server.infra.FrameworkResourcesServiceNameResolver;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * System service for managing {@link AmbientContextEvent}s.
+ */
+public class AmbientContextManagerService extends
+ AbstractMasterSystemService<AmbientContextManagerService,
+ AmbientContextManagerPerUserService> {
+ private static final String TAG = AmbientContextManagerService.class.getSimpleName();
+ private static final String KEY_SERVICE_ENABLED = "service_enabled";
+
+ /** Default value in absence of {@link DeviceConfig} override. */
+ private static final boolean DEFAULT_SERVICE_ENABLED = true;
+
+ private final Context mContext;
+ boolean mIsServiceEnabled;
+
+ public AmbientContextManagerService(Context context) {
+ super(context,
+ new FrameworkResourcesServiceNameResolver(
+ context,
+ R.string.config_defaultAmbientContextDetectionService),
+ /*disallowProperty=*/null,
+ PACKAGE_UPDATE_POLICY_REFRESH_EAGER
+ | /*To avoid high latency*/ PACKAGE_RESTART_POLICY_REFRESH_EAGER);
+ mContext = context;
+ }
+
+ @Override
+ public void onStart() {
+ publishBinderService(Context.AMBIENT_CONTEXT_SERVICE, new AmbientContextEventObserver());
+ }
+
+ @Override
+ public void onBootPhase(int phase) {
+ if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
+ DeviceConfig.addOnPropertiesChangedListener(
+ NAMESPACE_AMBIENT_CONTEXT_MANAGER_SERVICE,
+ getContext().getMainExecutor(),
+ (properties) -> onDeviceConfigChange(properties.getKeyset()));
+
+ mIsServiceEnabled = DeviceConfig.getBoolean(
+ NAMESPACE_AMBIENT_CONTEXT_MANAGER_SERVICE,
+ KEY_SERVICE_ENABLED, DEFAULT_SERVICE_ENABLED);
+ }
+ }
+
+ private void onDeviceConfigChange(@NonNull Set<String> keys) {
+ if (keys.contains(KEY_SERVICE_ENABLED)) {
+ mIsServiceEnabled = DeviceConfig.getBoolean(
+ NAMESPACE_AMBIENT_CONTEXT_MANAGER_SERVICE,
+ KEY_SERVICE_ENABLED, DEFAULT_SERVICE_ENABLED);
+ }
+ }
+
+ @Override
+ protected AmbientContextManagerPerUserService newServiceLocked(int resolvedUserId,
+ boolean disabled) {
+ return new AmbientContextManagerPerUserService(this, mLock, resolvedUserId);
+ }
+
+ @Override
+ protected void onServiceRemoved(
+ AmbientContextManagerPerUserService service, @UserIdInt int userId) {
+ service.destroyLocked();
+ }
+
+ /** Returns {@code true} if the detection service is configured on this device. */
+ public static boolean isDetectionServiceConfigured() {
+ final PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
+ final String[] packageNames = pmi.getKnownPackageNames(
+ PackageManagerInternal.PACKAGE_AMBIENT_CONTEXT_DETECTION, UserHandle.USER_SYSTEM);
+ boolean isServiceConfigured = (packageNames.length != 0);
+ Slog.i(TAG, "Detection service configured: " + isServiceConfigured);
+ return isServiceConfigured;
+ }
+
+ /**
+ * Send request to the remote AmbientContextDetectionService impl to start detecting the
+ * specified events. Intended for use by shell command for testing.
+ * Requires ACCESS_AMBIENT_CONTEXT_EVENT permission.
+ */
+ void startAmbientContextEvent(@UserIdInt int userId, AmbientContextEventRequest request,
+ String packageName, RemoteCallback callback) {
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT, TAG);
+ synchronized (mLock) {
+ final AmbientContextManagerPerUserService service = getServiceForUserLocked(userId);
+ if (service != null) {
+ service.startDetection(request, packageName, callback);
+ } else {
+ Slog.i(TAG, "service not available for user_id: " + userId);
+ }
+ }
+ }
+
+ /**
+ * Send request to the remote AmbientContextDetectionService impl to stop detecting the
+ * specified events. Intended for use by shell command for testing.
+ * Requires ACCESS_AMBIENT_CONTEXT_EVENT permission.
+ */
+ void stopAmbientContextEvent(@UserIdInt int userId, String packageName) {
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT, TAG);
+ synchronized (mLock) {
+ final AmbientContextManagerPerUserService service = getServiceForUserLocked(userId);
+ if (service != null) {
+ service.stopDetection(packageName);
+ } else {
+ Slog.i(TAG, "service not available for user_id: " + userId);
+ }
+ }
+ }
+
+ /**
+ * Returns the AmbientContextManagerPerUserService component for this user.
+ */
+ public ComponentName getComponentName(@UserIdInt int userId) {
+ synchronized (mLock) {
+ final AmbientContextManagerPerUserService service = getServiceForUserLocked(userId);
+ if (service != null) {
+ return service.getComponentName();
+ }
+ }
+ return null;
+ }
+
+ private final class AmbientContextEventObserver extends IAmbientContextEventObserver.Stub {
+ final AmbientContextManagerPerUserService mService = getServiceForUserLocked(
+ UserHandle.getCallingUserId());
+
+ @Override
+ public void registerObserver(
+ AmbientContextEventRequest request, PendingIntent pendingIntent) {
+ Objects.requireNonNull(request);
+ Objects.requireNonNull(pendingIntent);
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT, TAG);
+ if (!mIsServiceEnabled) {
+ Slog.w(TAG, "Service not available.");
+ mService.sendStatusUpdateIntent(pendingIntent,
+ AmbientContextEventResponse.STATUS_SERVICE_UNAVAILABLE);
+ return;
+ }
+ mService.onRegisterObserver(request, pendingIntent);
+ }
+
+ @Override
+ public void unregisterObserver(String callingPackage) {
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT, TAG);
+ mService.onUnregisterObserver(callingPackage);
+ }
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) {
+ return;
+ }
+ synchronized (mLock) {
+ dumpLocked("", pw);
+ }
+ }
+
+ @Override
+ public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
+ String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
+ new AmbientContextShellCommand(AmbientContextManagerService.this).exec(
+ this, in, out, err, args, callback, resultReceiver);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/ambientcontext/AmbientContextShellCommand.java b/services/core/java/com/android/server/ambientcontext/AmbientContextShellCommand.java
new file mode 100644
index 000000000000..b5cd985fa097
--- /dev/null
+++ b/services/core/java/com/android/server/ambientcontext/AmbientContextShellCommand.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 com.android.server.ambientcontext;
+
+import static java.lang.System.out;
+
+import android.annotation.NonNull;
+import android.app.ambientcontext.AmbientContextEvent;
+import android.app.ambientcontext.AmbientContextEventRequest;
+import android.app.ambientcontext.AmbientContextEventResponse;
+import android.content.ComponentName;
+import android.os.Binder;
+import android.os.RemoteCallback;
+import android.os.ShellCommand;
+import android.service.ambientcontext.AmbientContextDetectionService;
+
+import java.io.PrintWriter;
+
+/**
+ * Shell command for {@link AmbientContextManagerService}.
+ */
+final class AmbientContextShellCommand extends ShellCommand {
+
+ @NonNull
+ private final AmbientContextManagerService mService;
+
+ AmbientContextShellCommand(@NonNull AmbientContextManagerService service) {
+ mService = service;
+ }
+
+ /** Callbacks for AmbientContextEventService results used internally for testing. */
+ static class TestableCallbackInternal {
+ private AmbientContextEventResponse mLastResponse;
+
+ public AmbientContextEventResponse getLastResponse() {
+ return mLastResponse;
+ }
+
+ @NonNull
+ private RemoteCallback createRemoteCallback() {
+ return new RemoteCallback(result -> {
+ AmbientContextEventResponse response =
+ (AmbientContextEventResponse) result.get(
+ AmbientContextDetectionService.RESPONSE_BUNDLE_KEY);
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mLastResponse = response;
+ out.println("Response available: " + response);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ });
+ }
+ }
+
+ static final TestableCallbackInternal sTestableCallbackInternal =
+ new TestableCallbackInternal();
+
+ @Override
+ public int onCommand(String cmd) {
+ if (cmd == null) {
+ return handleDefaultCommands(cmd);
+ }
+
+ switch (cmd) {
+ case "start-detection":
+ return runStartDetection();
+ case "stop-detection":
+ return runStopDetection();
+ case "get-last-status-code":
+ return getLastStatusCode();
+ case "get-bound-package":
+ return getBoundPackageName();
+ case "set-temporary-service":
+ return setTemporaryService();
+ default:
+ return handleDefaultCommands(cmd);
+ }
+ }
+
+ private int runStartDetection() {
+ final int userId = Integer.parseInt(getNextArgRequired());
+ final String packageName = getNextArgRequired();
+ AmbientContextEventRequest request = new AmbientContextEventRequest.Builder()
+ .addEventType(AmbientContextEvent.EVENT_COUGH)
+ .addEventType(AmbientContextEvent.EVENT_SNORE)
+ .build();
+
+ mService.startAmbientContextEvent(userId, request, packageName,
+ sTestableCallbackInternal.createRemoteCallback());
+ return 0;
+ }
+
+ private int runStopDetection() {
+ final int userId = Integer.parseInt(getNextArgRequired());
+ final String packageName = getNextArgRequired();
+ mService.stopAmbientContextEvent(userId, packageName);
+ return 0;
+ }
+
+ private int getLastStatusCode() {
+ AmbientContextEventResponse lastResponse = sTestableCallbackInternal.getLastResponse();
+ if (lastResponse == null) {
+ return -1;
+ }
+ return lastResponse.getStatusCode();
+ }
+
+ @Override
+ public void onHelp() {
+ PrintWriter pw = getOutPrintWriter();
+ pw.println("AmbientContextEvent commands: ");
+ pw.println(" help");
+ pw.println(" Print this help text.");
+ pw.println();
+ pw.println(" start-detection USER_ID PACKAGE_NAME: Starts AmbientContextEvent detection.");
+ pw.println(" stop-detection USER_ID: Stops AmbientContextEvent detection.");
+ pw.println(" get-last-status-code: Prints the latest request status code.");
+ pw.println(" get-bound-package USER_ID:"
+ + " Print the bound package that implements the service.");
+ pw.println(" set-temporary-service USER_ID [COMPONENT_NAME DURATION]");
+ pw.println(" Temporarily (for DURATION ms) changes the service implementation.");
+ pw.println(" To reset, call with just the USER_ID argument.");
+ }
+
+ private int getBoundPackageName() {
+ final PrintWriter out = getOutPrintWriter();
+ final int userId = Integer.parseInt(getNextArgRequired());
+ final ComponentName componentName = mService.getComponentName(userId);
+ out.println(componentName == null ? "" : componentName.getPackageName());
+ return 0;
+ }
+
+ private int setTemporaryService() {
+ final PrintWriter out = getOutPrintWriter();
+ final int userId = Integer.parseInt(getNextArgRequired());
+ final String serviceName = getNextArg();
+ if (serviceName == null) {
+ mService.resetTemporaryService(userId);
+ out.println("AmbientContextDetectionService temporary reset. ");
+ return 0;
+ }
+
+ final int duration = Integer.parseInt(getNextArgRequired());
+ mService.setTemporaryService(userId, serviceName, duration);
+ out.println("AmbientContextDetectionService temporarily set to " + serviceName
+ + " for " + duration + "ms");
+ return 0;
+ }
+}
diff --git a/services/core/java/com/android/server/ambientcontext/OWNERS b/services/core/java/com/android/server/ambientcontext/OWNERS
new file mode 100644
index 000000000000..a863297b84e8
--- /dev/null
+++ b/services/core/java/com/android/server/ambientcontext/OWNERS
@@ -0,0 +1,3 @@
+enxun@google.com
+kxchen@google.com
+tgadh@google.com
diff --git a/services/core/java/com/android/server/ambientcontext/RemoteAmbientContextDetectionService.java b/services/core/java/com/android/server/ambientcontext/RemoteAmbientContextDetectionService.java
new file mode 100644
index 000000000000..5cc29b3c34c0
--- /dev/null
+++ b/services/core/java/com/android/server/ambientcontext/RemoteAmbientContextDetectionService.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.ambientcontext;
+
+import static android.content.Context.BIND_FOREGROUND_SERVICE;
+import static android.content.Context.BIND_INCLUDE_CAPABILITIES;
+
+import android.annotation.NonNull;
+import android.app.ambientcontext.AmbientContextEventRequest;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.RemoteCallback;
+import android.service.ambientcontext.AmbientContextDetectionService;
+import android.service.ambientcontext.IAmbientContextDetectionService;
+import android.util.Slog;
+
+import com.android.internal.infra.ServiceConnector;
+
+/** Manages the connection to the remote service. */
+final class RemoteAmbientContextDetectionService
+ extends ServiceConnector.Impl<IAmbientContextDetectionService> {
+ private static final String TAG =
+ RemoteAmbientContextDetectionService.class.getSimpleName();
+
+ RemoteAmbientContextDetectionService(Context context, ComponentName serviceName,
+ int userId) {
+ super(context, new Intent(
+ AmbientContextDetectionService.SERVICE_INTERFACE).setComponent(serviceName),
+ BIND_FOREGROUND_SERVICE | BIND_INCLUDE_CAPABILITIES, userId,
+ IAmbientContextDetectionService.Stub::asInterface);
+
+ // Bind right away
+ connect();
+ }
+
+ /**
+ * Asks the implementation to start detection.
+ *
+ * @param request The request with events to detect, and optional detection options.
+ * @param packageName The app package that requested the detection
+ * @param callback callback for detection results
+ */
+ public void startDetection(
+ @NonNull AmbientContextEventRequest request, String packageName,
+ RemoteCallback callback) {
+ Slog.i(TAG, "Start detection for " + request.getEventTypes());
+ post(service -> service.startDetection(request, packageName, callback));
+ }
+
+ /**
+ * Asks the implementation to stop detection.
+ *
+ * @param packageName stop detection for the given package
+ */
+ public void stopDetection(String packageName) {
+ Slog.i(TAG, "Stop detection for " + packageName);
+ post(service -> service.stopDetection(packageName));
+ }
+}
diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java
index 0980f4077489..f5f7bb3428bf 100644
--- a/services/core/java/com/android/server/app/GameManagerService.java
+++ b/services/core/java/com/android/server/app/GameManagerService.java
@@ -39,9 +39,11 @@ import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.GameManager;
import android.app.GameManager.GameMode;
+import android.app.GameModeInfo;
import android.app.GameState;
import android.app.IGameManagerService;
import android.app.compat.PackageOverride;
@@ -81,6 +83,7 @@ import com.android.server.LocalServices;
import com.android.server.ServiceThread;
import com.android.server.SystemService;
import com.android.server.SystemService.TargetUser;
+import com.android.server.app.GameManagerService.GamePackageConfiguration.GameModeConfiguration;
import java.io.FileDescriptor;
import java.util.List;
@@ -113,6 +116,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
private final Context mContext;
private final Object mLock = new Object();
private final Object mDeviceConfigLock = new Object();
+ private final Object mOverrideConfigLock = new Object();
private final Handler mHandler;
private final PackageManager mPackageManager;
private final IPlatformCompat mPlatformCompat;
@@ -122,6 +126,8 @@ public final class GameManagerService extends IGameManagerService.Stub {
private final ArrayMap<Integer, GameManagerSettings> mSettings = new ArrayMap<>();
@GuardedBy("mDeviceConfigLock")
private final ArrayMap<String, GamePackageConfiguration> mConfigs = new ArrayMap<>();
+ @GuardedBy("mOverrideConfigLock")
+ private final ArrayMap<String, GamePackageConfiguration> mOverrideConfigs = new ArrayMap<>();
public GameManagerService(Context context) {
this(context, createServiceThread().getLooper());
@@ -281,13 +287,51 @@ public final class GameManagerService extends IGameManagerService.Stub {
return 0;
}
+ public enum FrameRate {
+ FPS_DEFAULT(0),
+ FPS_30(30),
+ FPS_45(45),
+ FPS_60(60),
+ FPS_90(90),
+ FPS_120(120),
+ FPS_INVALID(-1);
+
+ public final int fps;
+
+ FrameRate(int fps) {
+ this.fps = fps;
+ }
+ }
+
+ // Turn the raw string to the corresponding fps int.
+ // Return 0 when disabling, -1 for invalid fps.
+ static int getFpsInt(String raw) {
+ switch (raw) {
+ case "30":
+ return FrameRate.FPS_30.fps;
+ case "45":
+ return FrameRate.FPS_45.fps;
+ case "60":
+ return FrameRate.FPS_60.fps;
+ case "90":
+ return FrameRate.FPS_90.fps;
+ case "120":
+ return FrameRate.FPS_120.fps;
+ case "disable":
+ case "":
+ return FrameRate.FPS_DEFAULT.fps;
+ }
+ return FrameRate.FPS_INVALID.fps;
+ }
+
/**
* Called by games to communicate the current state to the platform.
* @param packageName The client package name.
* @param gameState An object set to the current state.
* @param userId The user associated with this state.
*/
- public void setGameState(String packageName, @NonNull GameState gameState, int userId) {
+ public void setGameState(String packageName, @NonNull GameState gameState,
+ @UserIdInt int userId) {
if (!isPackageGame(packageName, userId)) {
// Restrict to games only.
return;
@@ -399,11 +443,14 @@ public final class GameManagerService extends IGameManagerService.Stub {
public static final String TAG = "GameManagerService_GameModeConfiguration";
public static final String MODE_KEY = "mode";
public static final String SCALING_KEY = "downscaleFactor";
+ public static final String FPS_KEY = "fps";
public static final String DEFAULT_SCALING = "1.0";
+ public static final String DEFAULT_FPS = "";
public static final String ANGLE_KEY = "useAngle";
private final @GameMode int mGameMode;
- private final String mScaling;
+ private String mScaling;
+ private String mFps;
private final boolean mUseAngle;
GameModeConfiguration(KeyValueListParser parser) {
@@ -414,6 +461,8 @@ public final class GameManagerService extends IGameManagerService.Stub {
// using ANGLE).
mScaling = !mAllowDownscale || willGamePerformOptimizations(mGameMode)
? DEFAULT_SCALING : parser.getString(SCALING_KEY, DEFAULT_SCALING);
+
+ mFps = parser.getString(FPS_KEY, DEFAULT_FPS);
// 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
@@ -430,14 +479,27 @@ public final class GameManagerService extends IGameManagerService.Stub {
return mScaling;
}
+ public int getFps() {
+ return GameManagerService.getFpsInt(mFps);
+ }
+
public boolean getUseAngle() {
return mUseAngle;
}
+ public void setScaling(String scaling) {
+ mScaling = scaling;
+ }
+
+ public void setFpsStr(String fpsStr) {
+ mFps = fpsStr;
+ }
+
public boolean isValid() {
- return mGameMode == GameManager.GAME_MODE_STANDARD
+ return (mGameMode == GameManager.GAME_MODE_STANDARD
|| mGameMode == GameManager.GAME_MODE_PERFORMANCE
- || mGameMode == GameManager.GAME_MODE_BATTERY;
+ || mGameMode == GameManager.GAME_MODE_BATTERY)
+ && !willGamePerformOptimizations(mGameMode);
}
/**
@@ -445,7 +507,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
*/
public String toString() {
return "[Game Mode:" + mGameMode + ",Scaling:" + mScaling + ",Use Angle:"
- + mUseAngle + "]";
+ + mUseAngle + ",Fps:" + mFps + "]";
}
/**
@@ -633,6 +695,32 @@ public final class GameManagerService extends IGameManagerService.Stub {
}
}
+ private @GameMode int[] getAvailableGameModesUnchecked(String packageName) {
+ GamePackageConfiguration config = null;
+ synchronized (mOverrideConfigLock) {
+ config = mOverrideConfigs.get(packageName);
+ }
+ if (config == null) {
+ synchronized (mDeviceConfigLock) {
+ config = mConfigs.get(packageName);
+ }
+ }
+ if (config == null) {
+ return new int[]{};
+ }
+ return config.getAvailableGameModes();
+ }
+
+ private boolean isPackageGame(String packageName, @UserIdInt int userId) {
+ try {
+ final ApplicationInfo applicationInfo = mPackageManager
+ .getApplicationInfoAsUser(packageName, PackageManager.MATCH_ALL, userId);
+ return applicationInfo.category == ApplicationInfo.CATEGORY_GAME;
+ } catch (PackageManager.NameNotFoundException e) {
+ return false;
+ }
+ }
+
/**
* Get an array of game modes available for a given package.
* Checks that the caller has {@link android.Manifest.permission#MANAGE_GAME_MODE}.
@@ -641,16 +729,10 @@ public final class GameManagerService extends IGameManagerService.Stub {
@RequiresPermission(Manifest.permission.MANAGE_GAME_MODE)
public @GameMode int[] getAvailableGameModes(String packageName) throws SecurityException {
checkPermission(Manifest.permission.MANAGE_GAME_MODE);
- synchronized (mDeviceConfigLock) {
- final GamePackageConfiguration config = mConfigs.get(packageName);
- if (config == null) {
- return new int[]{GameManager.GAME_MODE_UNSUPPORTED};
- }
- return config.getAvailableGameModes();
- }
+ return getAvailableGameModesUnchecked(packageName);
}
- private @GameMode int getGameModeFromSettings(String packageName, int userId) {
+ private @GameMode int getGameModeFromSettings(String packageName, @UserIdInt int userId) {
synchronized (mLock) {
if (!mSettings.containsKey(userId)) {
Slog.w(TAG, "User ID '" + userId + "' does not have a Game Mode"
@@ -668,28 +750,22 @@ public final class GameManagerService extends IGameManagerService.Stub {
* {@link android.Manifest.permission#MANAGE_GAME_MODE}.
*/
@Override
- public @GameMode int getGameMode(String packageName, int userId)
+ public @GameMode int getGameMode(@NonNull String packageName, @UserIdInt int userId)
throws SecurityException {
userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
Binder.getCallingUid(), userId, false, true, "getGameMode",
"com.android.server.app.GameManagerService");
// Restrict to games only.
- try {
- final ApplicationInfo applicationInfo = mPackageManager
- .getApplicationInfoAsUser(packageName, PackageManager.MATCH_ALL, userId);
- if (applicationInfo.category != ApplicationInfo.CATEGORY_GAME) {
- // The game mode for applications that are not identified as game is always
- // UNSUPPORTED. See {@link PackageManager#setApplicationCategoryHint(String, int)}
- return GameManager.GAME_MODE_UNSUPPORTED;
- }
- } catch (PackageManager.NameNotFoundException e) {
+ if (!isPackageGame(packageName, userId)) {
+ // The game mode for applications that are not identified as game is always
+ // UNSUPPORTED. See {@link PackageManager#setApplicationCategoryHint(String, int)}
return GameManager.GAME_MODE_UNSUPPORTED;
}
// This function handles two types of queries:
- // 1.) A normal, non-privileged app querying its own Game Mode.
- // 2.) A privileged system service querying the Game Mode of another package.
+ // 1) A normal, non-privileged app querying its own Game Mode.
+ // 2) A privileged system service querying the Game Mode of another package.
// The least privileged case is a normal app performing a query, so check that first and
// return a value if the package name is valid. Next, check if the caller has the necessary
// permission and return a value. Do this check last, since it can throw an exception.
@@ -702,14 +778,32 @@ public final class GameManagerService extends IGameManagerService.Stub {
return getGameModeFromSettings(packageName, userId);
}
- private boolean isPackageGame(String packageName, int userId) {
- try {
- final ApplicationInfo applicationInfo = mPackageManager
- .getApplicationInfoAsUser(packageName, PackageManager.MATCH_ALL, userId);
- return applicationInfo.category == ApplicationInfo.CATEGORY_GAME;
- } catch (PackageManager.NameNotFoundException e) {
- return false;
+ /**
+ * Get the GameModeInfo for the package name.
+ * Verifies that the calling process is for the matching package UID or has
+ * {@link android.Manifest.permission#MANAGE_GAME_MODE}. If the package is not a game,
+ * null is always returned.
+ */
+ @Override
+ @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE)
+ @Nullable
+ public GameModeInfo getGameModeInfo(@NonNull String packageName, @UserIdInt int userId) {
+ userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
+ Binder.getCallingUid(), userId, false, true, "getGameModeInfo",
+ "com.android.server.app.GameManagerService");
+
+ // Check the caller has the necessary permission.
+ checkPermission(Manifest.permission.MANAGE_GAME_MODE);
+
+ // Restrict to games only.
+ if (!isPackageGame(packageName, userId)) {
+ return null;
}
+
+ final @GameMode int activeGameMode = getGameModeFromSettings(packageName, userId);
+ final @GameMode int[] availableGameModes = getAvailableGameModesUnchecked(packageName);
+
+ return new GameModeInfo(activeGameMode, availableGameModes);
}
/**
@@ -743,7 +837,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
mHandler.sendMessageDelayed(msg, WRITE_SETTINGS_DELAY);
}
}
- updateInterventions(packageName, gameMode);
+ updateInterventions(packageName, gameMode, userId);
}
/**
@@ -876,41 +970,26 @@ public final class GameManagerService extends IGameManagerService.Stub {
}
}
- private void updateCompatModeDownscale(String packageName, @GameMode int gameMode) {
- synchronized (mDeviceConfigLock) {
- if (gameMode == GameManager.GAME_MODE_STANDARD
- || gameMode == GameManager.GAME_MODE_UNSUPPORTED) {
- disableCompatScale(packageName);
- return;
- }
- final GamePackageConfiguration packageConfig = mConfigs.get(packageName);
- if (packageConfig == null) {
- disableCompatScale(packageName);
- Slog.v(TAG, "Package configuration not found for " + packageName);
- return;
- }
- if (DEBUG) {
- Slog.v(TAG, dumpDeviceConfigs());
- }
- if (packageConfig.willGamePerformOptimizations(gameMode)) {
- disableCompatScale(packageName);
- return;
- }
- final GamePackageConfiguration.GameModeConfiguration modeConfig =
- packageConfig.getGameModeConfiguration(gameMode);
- if (modeConfig == null) {
- Slog.i(TAG, "Game mode " + gameMode + " not found for " + packageName);
- return;
- }
- long scaleId = modeConfig.getCompatChangeId();
- if (scaleId == 0) {
- Slog.i(TAG, "Invalid downscaling change id " + scaleId + " for "
- + packageName);
- return;
- }
+ private void updateCompatModeDownscale(GamePackageConfiguration packageConfig,
+ String packageName, @GameMode int gameMode) {
- enableCompatScale(packageName, scaleId);
+ if (DEBUG) {
+ Slog.v(TAG, dumpDeviceConfigs());
+ }
+ final GamePackageConfiguration.GameModeConfiguration modeConfig =
+ packageConfig.getGameModeConfiguration(gameMode);
+ if (modeConfig == null) {
+ Slog.i(TAG, "Game mode " + gameMode + " not found for " + packageName);
+ return;
}
+ long scaleId = modeConfig.getCompatChangeId();
+ if (scaleId == 0) {
+ Slog.w(TAG, "Invalid downscaling change id " + scaleId + " for "
+ + packageName);
+ return;
+ }
+
+ enableCompatScale(packageName, scaleId);
}
private int modeToBitmask(@GameMode int gameMode) {
@@ -927,16 +1006,233 @@ public final class GameManagerService extends IGameManagerService.Stub {
// ship.
}
- private void updateInterventions(String packageName, @GameMode int gameMode) {
- updateCompatModeDownscale(packageName, gameMode);
+
+ private void updateFps(GamePackageConfiguration packageConfig, String packageName,
+ @GameMode int gameMode, @UserIdInt int userId) {
+ final GamePackageConfiguration.GameModeConfiguration modeConfig =
+ packageConfig.getGameModeConfiguration(gameMode);
+ if (modeConfig == null) {
+ Slog.d(TAG, "Game mode " + gameMode + " not found for " + packageName);
+ return;
+ }
+ try {
+ final float fps = modeConfig.getFps();
+ final int uid = mPackageManager.getPackageUidAsUser(packageName, userId);
+ nativeSetOverrideFrameRate(uid, fps);
+ } catch (PackageManager.NameNotFoundException e) {
+ return;
+ }
+ }
+
+
+ private void updateInterventions(String packageName,
+ @GameMode int gameMode, @UserIdInt int userId) {
+ if (gameMode == GameManager.GAME_MODE_STANDARD
+ || gameMode == GameManager.GAME_MODE_UNSUPPORTED) {
+ disableCompatScale(packageName);
+ return;
+ }
+ GamePackageConfiguration packageConfig = null;
+
+ synchronized (mOverrideConfigLock) {
+ packageConfig = mOverrideConfigs.get(packageName);
+ }
+
+ if (packageConfig == null) {
+ synchronized (mDeviceConfigLock) {
+ packageConfig = mConfigs.get(packageName);
+ }
+ }
+
+ if (packageConfig == null) {
+ disableCompatScale(packageName);
+ Slog.v(TAG, "Package configuration not found for " + packageName);
+ return;
+ }
+ if (packageConfig.willGamePerformOptimizations(gameMode)) {
+ return;
+ }
+ updateCompatModeDownscale(packageConfig, packageName, gameMode);
+ updateFps(packageConfig, packageName, gameMode, userId);
updateUseAngle(packageName, gameMode);
}
/**
+ * Set the override Game Mode Configuration.
+ * Update the config if exists, create one if not.
+ */
+ @VisibleForTesting
+ @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE)
+ public void setGameModeConfigOverride(String packageName, @UserIdInt int userId,
+ @GameMode int gameMode, String fpsStr, String scaling) throws SecurityException {
+ checkPermission(Manifest.permission.MANAGE_GAME_MODE);
+ synchronized (mLock) {
+ if (!mSettings.containsKey(userId)) {
+ return;
+ }
+ }
+ // Adding override game mode configuration of the given package name
+ synchronized (mOverrideConfigLock) {
+ // look for the existing override GamePackageConfiguration
+ GamePackageConfiguration overrideConfig = mOverrideConfigs.get(packageName);
+ if (overrideConfig == null) {
+ overrideConfig = new GamePackageConfiguration(packageName, userId);
+ mOverrideConfigs.put(packageName, overrideConfig);
+ }
+
+ // modify GameModeConfiguration intervention settings
+ GamePackageConfiguration.GameModeConfiguration overrideModeConfig =
+ overrideConfig.getGameModeConfiguration(gameMode);
+
+ if (fpsStr != null) {
+ overrideModeConfig.setFpsStr(fpsStr);
+ } else {
+ overrideModeConfig.setFpsStr(
+ GamePackageConfiguration.GameModeConfiguration.DEFAULT_FPS);
+ }
+ if (scaling != null) {
+ overrideModeConfig.setScaling(scaling);
+ } else {
+ overrideModeConfig.setScaling(
+ GamePackageConfiguration.GameModeConfiguration.DEFAULT_SCALING);
+ }
+ Slog.i(TAG, "Package Name: " + packageName
+ + " FPS: " + String.valueOf(overrideModeConfig.getFps())
+ + " Scaling: " + overrideModeConfig.getScaling());
+ }
+ setGameMode(packageName, gameMode, userId);
+ }
+
+ /**
+ * Reset the overridden gameModeConfiguration of the given mode.
+ * Remove the override config if game mode is not specified.
+ */
+ @VisibleForTesting
+ @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE)
+ public void resetGameModeConfigOverride(String packageName, @UserIdInt int userId,
+ @GameMode int gameModeToReset) throws SecurityException {
+ checkPermission(Manifest.permission.MANAGE_GAME_MODE);
+ synchronized (mLock) {
+ if (!mSettings.containsKey(userId)) {
+ return;
+ }
+ }
+
+ // resets GamePackageConfiguration of a given packageName.
+ // If a gameMode is specified, only reset the GameModeConfiguration of the gameMode.
+ if (gameModeToReset != -1) {
+ GamePackageConfiguration overrideConfig = null;
+ synchronized (mOverrideConfigLock) {
+ overrideConfig = mOverrideConfigs.get(packageName);
+ }
+
+ GamePackageConfiguration config = null;
+ synchronized (mDeviceConfigLock) {
+ config = mConfigs.get(packageName);
+ }
+
+ int[] modes = overrideConfig.getAvailableGameModes();
+
+ // First check if the mode to reset exists
+ boolean isGameModeExist = false;
+ for (int mode : modes) {
+ if (gameModeToReset == mode) {
+ isGameModeExist = true;
+ }
+ }
+ if (!isGameModeExist) {
+ return;
+ }
+
+ // If the game mode to reset is the only mode other than standard mode,
+ // The override config is removed.
+ if (modes.length <= 2) {
+ synchronized (mOverrideConfigLock) {
+ mOverrideConfigs.remove(packageName);
+ }
+ } else {
+ // otherwise we reset the mode by copying the original config.
+ overrideConfig.addModeConfig(config.getGameModeConfiguration(gameModeToReset));
+ }
+ } else {
+ synchronized (mOverrideConfigLock) {
+ // remove override config if there is one
+ mOverrideConfigs.remove(packageName);
+ }
+ }
+
+ // Make sure after resetting the game mode is still supported.
+ // If not, set the game mode to standard
+ int gameMode = getGameMode(packageName, userId);
+ int newGameMode = gameMode;
+
+ GamePackageConfiguration config = null;
+ synchronized (mOverrideConfigLock) {
+ config = mOverrideConfigs.get(packageName);
+ }
+ synchronized (mDeviceConfigLock) {
+ config = mConfigs.get(packageName);
+ }
+ if (config != null) {
+ int modesBitfield = config.getAvailableGameModesBitfield();
+ // Remove UNSUPPORTED to simplify the logic here, since we really just
+ // want to check if we support selectable game modes
+ modesBitfield &= ~modeToBitmask(GameManager.GAME_MODE_UNSUPPORTED);
+ if (!bitFieldContainsModeBitmask(modesBitfield, gameMode)) {
+ if (bitFieldContainsModeBitmask(modesBitfield,
+ GameManager.GAME_MODE_STANDARD)) {
+ // If the current set mode isn't supported,
+ // but we support STANDARD, then set the mode to STANDARD.
+ newGameMode = GameManager.GAME_MODE_STANDARD;
+ } else {
+ // If we don't support any game modes, then set to UNSUPPORTED
+ newGameMode = GameManager.GAME_MODE_UNSUPPORTED;
+ }
+ }
+ } else if (gameMode != GameManager.GAME_MODE_UNSUPPORTED) {
+ // If we have no config for the package, but the configured mode is not
+ // UNSUPPORTED, then set to UNSUPPORTED
+ newGameMode = GameManager.GAME_MODE_UNSUPPORTED;
+ }
+ if (gameMode != newGameMode) {
+ setGameMode(packageName, GameManager.GAME_MODE_STANDARD, userId);
+ return;
+ }
+ setGameMode(packageName, gameMode, userId);
+ }
+
+ /**
+ * Returns the string listing all the interventions currently set to a game.
+ */
+ public String getInterventionList(String packageName) {
+ GamePackageConfiguration packageConfig = null;
+ synchronized (mOverrideConfigLock) {
+ packageConfig = mOverrideConfigs.get(packageName);
+ }
+
+ if (packageConfig == null) {
+ synchronized (mDeviceConfigLock) {
+ packageConfig = mConfigs.get(packageName);
+ }
+ }
+
+ StringBuilder listStrSb = new StringBuilder();
+ if (packageConfig == null) {
+ listStrSb.append("\n No intervention found for package ")
+ .append(packageName);
+ return listStrSb.toString();
+ }
+ listStrSb.append("\nPackage name: ")
+ .append(packageName)
+ .append(packageConfig.toString());
+ return listStrSb.toString();
+ }
+
+ /**
* @hide
*/
@VisibleForTesting
- void updateConfigsForUser(int userId, String ...packageNames) {
+ void updateConfigsForUser(@UserIdInt int userId, String ...packageNames) {
try {
synchronized (mDeviceConfigLock) {
for (final String packageName : packageNames) {
@@ -954,43 +1250,47 @@ public final class GameManagerService extends IGameManagerService.Stub {
}
}
}
+ synchronized (mLock) {
+ if (!mSettings.containsKey(userId)) {
+ return;
+ }
+ }
for (final String packageName : packageNames) {
- if (mSettings.containsKey(userId)) {
- int gameMode = getGameMode(packageName, userId);
- int newGameMode = gameMode;
- // Make sure the user settings and package configs don't conflict. I.e. the
- // user setting is set to a mode that no longer available due to config/manifest
- // changes. Most of the time we won't have to change anything.
- GamePackageConfiguration config;
- synchronized (mDeviceConfigLock) {
- config = mConfigs.get(packageName);
- }
- if (config != null) {
- int modesBitfield = config.getAvailableGameModesBitfield();
- // Remove UNSUPPORTED to simplify the logic here, since we really just
- // want to check if we support selectable game modes
- modesBitfield &= ~modeToBitmask(GameManager.GAME_MODE_UNSUPPORTED);
- if (!bitFieldContainsModeBitmask(modesBitfield, gameMode)) {
- if (bitFieldContainsModeBitmask(modesBitfield,
- GameManager.GAME_MODE_STANDARD)) {
- // If the current set mode isn't supported, but we support STANDARD,
- // then set the mode to STANDARD.
- newGameMode = GameManager.GAME_MODE_STANDARD;
- } else {
- // If we don't support any game modes, then set to UNSUPPORTED
- newGameMode = GameManager.GAME_MODE_UNSUPPORTED;
- }
+ int gameMode = getGameMode(packageName, userId);
+ int newGameMode = gameMode;
+ // Make sure the user settings and package configs don't conflict.
+ // I.e. the user setting is set to a mode that no longer available due to
+ // config/manifest changes.
+ // Most of the time we won't have to change anything.
+ GamePackageConfiguration config = null;
+ synchronized (mDeviceConfigLock) {
+ config = mConfigs.get(packageName);
+ }
+ if (config != null) {
+ int modesBitfield = config.getAvailableGameModesBitfield();
+ // Remove UNSUPPORTED to simplify the logic here, since we really just
+ // want to check if we support selectable game modes
+ modesBitfield &= ~modeToBitmask(GameManager.GAME_MODE_UNSUPPORTED);
+ if (!bitFieldContainsModeBitmask(modesBitfield, gameMode)) {
+ if (bitFieldContainsModeBitmask(modesBitfield,
+ GameManager.GAME_MODE_STANDARD)) {
+ // If the current set mode isn't supported,
+ // but we support STANDARD, then set the mode to STANDARD.
+ newGameMode = GameManager.GAME_MODE_STANDARD;
+ } else {
+ // If we don't support any game modes, then set to UNSUPPORTED
+ newGameMode = GameManager.GAME_MODE_UNSUPPORTED;
}
- } else if (gameMode != GameManager.GAME_MODE_UNSUPPORTED) {
- // If we have no config for the package, but the configured mode is not
- // UNSUPPORTED, then set to UNSUPPORTED
- newGameMode = GameManager.GAME_MODE_UNSUPPORTED;
- }
- if (newGameMode != gameMode) {
- setGameMode(packageName, newGameMode, userId);
}
- updateInterventions(packageName, gameMode);
+ } else if (gameMode != GameManager.GAME_MODE_UNSUPPORTED) {
+ // If we have no config for the package, but the configured mode is not
+ // UNSUPPORTED, then set to UNSUPPORTED
+ newGameMode = GameManager.GAME_MODE_UNSUPPORTED;
+ }
+ if (newGameMode != gameMode) {
+ setGameMode(packageName, newGameMode, userId);
}
+ updateInterventions(packageName, gameMode, userId);
}
} catch (Exception e) {
Slog.e(TAG, "Failed to update compat modes for user " + userId + ": " + e);
@@ -1011,7 +1311,16 @@ public final class GameManagerService extends IGameManagerService.Stub {
*/
@VisibleForTesting
public GamePackageConfiguration getConfig(String packageName) {
- return mConfigs.get(packageName);
+ GamePackageConfiguration packageConfig = null;
+ synchronized (mOverrideConfigLock) {
+ packageConfig = mOverrideConfigs.get(packageName);
+ }
+ if (packageConfig == null) {
+ synchronized (mDeviceConfigLock) {
+ packageConfig = mConfigs.get(packageName);
+ }
+ }
+ return packageConfig;
}
private void registerPackageReceiver() {
@@ -1047,6 +1356,9 @@ public final class GameManagerService extends IGameManagerService.Stub {
break;
case ACTION_PACKAGE_REMOVED:
disableCompatScale(packageName);
+ synchronized (mOverrideConfigLock) {
+ mOverrideConfigs.remove(packageName);
+ }
synchronized (mDeviceConfigLock) {
mConfigs.remove(packageName);
}
@@ -1083,4 +1395,9 @@ public final class GameManagerService extends IGameManagerService.Stub {
handlerThread.start();
return handlerThread;
}
+
+ /**
+ * load dynamic library for frame rate overriding JNI calls
+ */
+ private static native void nativeSetOverrideFrameRate(int uid, float frameRate);
}
diff --git a/services/core/java/com/android/server/app/GameManagerShellCommand.java b/services/core/java/com/android/server/app/GameManagerShellCommand.java
index f07d207e7d06..470c320dbd7d 100644
--- a/services/core/java/com/android/server/app/GameManagerShellCommand.java
+++ b/services/core/java/com/android/server/app/GameManagerShellCommand.java
@@ -34,7 +34,6 @@ import static com.android.server.wm.CompatModePackages.DOWNSCALE_90;
import android.app.ActivityManager;
import android.app.GameManager;
import android.app.IGameManagerService;
-import android.compat.Compatibility;
import android.content.Context;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -42,12 +41,8 @@ import android.os.ServiceManager.ServiceNotFoundException;
import android.os.ShellCommand;
import android.util.ArraySet;
-import com.android.internal.compat.CompatibilityChangeConfig;
-import com.android.server.compat.PlatformCompat;
-
import java.io.PrintWriter;
-import java.util.Set;
-import java.util.stream.Collectors;
+import java.util.Locale;
/**
* ShellCommands for GameManagerService.
@@ -83,42 +78,11 @@ public class GameManagerShellCommand extends ShellCommand {
final PrintWriter pw = getOutPrintWriter();
try {
switch (cmd) {
- case "downscale": {
- final String ratio = getNextArgRequired();
- final String packageName = getNextArgRequired();
-
- final long changeId = GameManagerService.getCompatChangeId(ratio);
- if (changeId == 0 && !ratio.equals("disable")) {
- pw.println("Invalid scaling ratio '" + ratio + "'");
- break;
- }
-
- Set<Long> enabled = new ArraySet<>();
- Set<Long> disabled;
- if (changeId == 0) {
- disabled = DOWNSCALE_CHANGE_IDS;
- } else {
- enabled.add(DOWNSCALED);
- enabled.add(changeId);
- disabled = DOWNSCALE_CHANGE_IDS.stream()
- .filter(it -> it != DOWNSCALED && it != changeId)
- .collect(Collectors.toSet());
- }
-
- final PlatformCompat platformCompat = (PlatformCompat)
- ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE);
- final CompatibilityChangeConfig overrides =
- new CompatibilityChangeConfig(
- new Compatibility.ChangeConfig(enabled, disabled));
-
- platformCompat.setOverrides(overrides, packageName);
- if (changeId == 0) {
- pw.println("Disable downscaling for " + packageName + ".");
- } else {
- pw.println("Enable downscaling ratio for " + packageName + " to " + ratio);
- }
-
- return 0;
+ case "set": {
+ return runSetGameMode(pw);
+ }
+ case "reset": {
+ return runResetGameMode(pw);
}
case "mode": {
/** The "mode" command allows setting a package's current game mode outside of
@@ -132,6 +96,9 @@ public class GameManagerShellCommand extends ShellCommand {
*/
return runGameMode(pw);
}
+ case "list": {
+ return runGameList(pw);
+ }
default:
return handleDefaultCommands(cmd);
}
@@ -141,6 +108,22 @@ public class GameManagerShellCommand extends ShellCommand {
return -1;
}
+ private int runGameList(PrintWriter pw) throws ServiceNotFoundException, RemoteException {
+ final String packageName = getNextArgRequired();
+
+ final GameManagerService gameManagerService = (GameManagerService)
+ ServiceManager.getService(Context.GAME_SERVICE);
+
+ final String listStr = gameManagerService.getInterventionList(packageName);
+
+ if (listStr == null) {
+ pw.println("No interventions found for " + packageName);
+ } else {
+ pw.println(packageName + " interventions: " + listStr);
+ }
+ return 0;
+ }
+
private int runGameMode(PrintWriter pw) throws ServiceNotFoundException, RemoteException {
final String option = getNextOption();
String userIdStr = null;
@@ -200,6 +183,172 @@ public class GameManagerShellCommand extends ShellCommand {
return 0;
}
+ private int runSetGameMode(PrintWriter pw) throws ServiceNotFoundException, RemoteException {
+ String option = getNextArgRequired();
+ if (!option.equals("--mode")) {
+ pw.println("Invalid option '" + option + "'");
+ return -1;
+ }
+
+ final String gameMode = getNextArgRequired();
+
+ /**
+ * handling optional input
+ * "--user", "--downscale" and "--fps" can come in any order
+ */
+ String userIdStr = null;
+ String fpsStr = null;
+ String downscaleRatio = null;
+ while ((option = getNextOption()) != null) {
+ switch (option) {
+ case "--user":
+ if (userIdStr == null) {
+ userIdStr = getNextArgRequired();
+ } else {
+ pw.println("Duplicate option '" + option + "'");
+ return -1;
+ }
+ break;
+ case "--downscale":
+ if (downscaleRatio == null) {
+ downscaleRatio = getNextArgRequired();
+ if (downscaleRatio != null
+ && GameManagerService.getCompatChangeId(downscaleRatio) == 0
+ && !downscaleRatio.equals("disable")) {
+ pw.println("Invalid scaling ratio '" + downscaleRatio + "'");
+ return -1;
+ }
+ } else {
+ pw.println("Duplicate option '" + option + "'");
+ return -1;
+ }
+ break;
+ case "--fps":
+ if (fpsStr == null) {
+ fpsStr = getNextArgRequired();
+ if (fpsStr != null && GameManagerService.getFpsInt(fpsStr) == -1) {
+ pw.println("Invalid frame rate '" + fpsStr + "'");
+ return -1;
+ }
+ } else {
+ pw.println("Duplicate option '" + option + "'");
+ return -1;
+ }
+ break;
+ default:
+ pw.println("Invalid option '" + option + "'");
+ return -1;
+ }
+ }
+
+ final String packageName = getNextArgRequired();
+
+ int userId = userIdStr != null ? Integer.parseInt(userIdStr)
+ : ActivityManager.getCurrentUser();
+
+ final GameManagerService gameManagerService = (GameManagerService)
+ ServiceManager.getService(Context.GAME_SERVICE);
+
+ boolean batteryModeSupported = false;
+ boolean perfModeSupported = false;
+ int [] modes = gameManagerService.getAvailableGameModes(packageName);
+
+ for (int mode : modes) {
+ if (mode == GameManager.GAME_MODE_PERFORMANCE) {
+ perfModeSupported = true;
+ } else if (mode == GameManager.GAME_MODE_BATTERY) {
+ batteryModeSupported = true;
+ }
+ }
+
+ switch (gameMode.toLowerCase(Locale.getDefault())) {
+ case "2":
+ case "performance":
+ if (perfModeSupported) {
+ gameManagerService.setGameModeConfigOverride(packageName, userId,
+ GameManager.GAME_MODE_PERFORMANCE, fpsStr, downscaleRatio);
+ } else {
+ pw.println("Game mode: " + gameMode + " not supported by "
+ + packageName);
+ return -1;
+ }
+ break;
+ case "3":
+ case "battery":
+ if (batteryModeSupported) {
+ gameManagerService.setGameModeConfigOverride(packageName, userId,
+ GameManager.GAME_MODE_BATTERY, fpsStr, downscaleRatio);
+ } else {
+ pw.println("Game mode: " + gameMode + " not supported by "
+ + packageName);
+ return -1;
+ }
+ break;
+ default:
+ pw.println("Invalid game mode: " + gameMode);
+ return -1;
+ }
+ return 0;
+ }
+
+ private int runResetGameMode(PrintWriter pw) throws ServiceNotFoundException, RemoteException {
+ String option = null;
+ String gameMode = null;
+ String userIdStr = null;
+ while ((option = getNextOption()) != null) {
+ switch (option) {
+ case "--user":
+ if (userIdStr == null) {
+ userIdStr = getNextArgRequired();
+ } else {
+ pw.println("Duplicate option '" + option + "'");
+ return -1;
+ }
+ break;
+ case "--mode":
+ if (gameMode == null) {
+ gameMode = getNextArgRequired();
+ } else {
+ pw.println("Duplicate option '" + option + "'");
+ return -1;
+ }
+ break;
+ default:
+ pw.println("Invalid option '" + option + "'");
+ return -1;
+ }
+ }
+
+ final String packageName = getNextArgRequired();
+
+ final GameManagerService gameManagerService = (GameManagerService)
+ ServiceManager.getService(Context.GAME_SERVICE);
+
+ int userId = userIdStr != null ? Integer.parseInt(userIdStr)
+ : ActivityManager.getCurrentUser();
+
+ if (gameMode == null) {
+ gameManagerService.resetGameModeConfigOverride(packageName, userId, -1);
+ return 0;
+ }
+
+ switch (gameMode.toLowerCase(Locale.getDefault())) {
+ case "2":
+ case "performance":
+ gameManagerService.resetGameModeConfigOverride(packageName, userId,
+ GameManager.GAME_MODE_PERFORMANCE);
+ break;
+ case "3":
+ case "battery":
+ gameManagerService.resetGameModeConfigOverride(packageName, userId,
+ GameManager.GAME_MODE_BATTERY);
+ break;
+ default:
+ pw.println("Invalid game mode: " + gameMode);
+ return -1;
+ }
+ return 0;
+ }
@Override
public void onHelp() {
@@ -207,10 +356,25 @@ public class GameManagerShellCommand extends ShellCommand {
pw.println("Game manager (game) commands:");
pw.println(" help");
pw.println(" Print this help text.");
- pw.println(" downscale [0.3|0.35|0.4|0.45|0.5|0.55|0.6|0.65|0.7|0.75|0.8|0.85|0.9|disable] <PACKAGE_NAME>");
- pw.println(" Force app to run at the specified scaling ratio.");
+ pw.println(" downscale");
+ pw.println(" Deprecated. Please use `set` command.");
pw.println(" mode [--user <USER_ID>] [1|2|3|standard|performance|battery] <PACKAGE_NAME>");
- pw.println(" Force app to run in the specified game mode, if supported.");
- pw.println(" --user <USER_ID>: apply for the given user, the current user is used when unspecified.");
+ pw.println(" Set app to run in the specified game mode, if supported.");
+ pw.println(" --user <USER_ID>: apply for the given user,");
+ pw.println(" the current user is used when unspecified.");
+ pw.println(" set --mode [2|3|performance|battery] [intervention configs] <PACKAGE_NAME>");
+ pw.println(" Set app to run at given game mode with configs, if supported.");
+ pw.println(" Intervention configs consists of:");
+ pw.println(" --downscale [0.3|0.35|0.4|0.45|0.5|0.55|0.6|0.65");
+ pw.println(" |0.7|0.75|0.8|0.85|0.9|disable]");
+ pw.println(" Set app to run at the specified scaling ratio.");
+ pw.println(" --fps [30|45|60|90|120|disable]");
+ pw.println(" Set app to run at the specified fps, if supported.");
+ pw.println(" reset [--mode [2|3|performance|battery] --user <USER_ID>] <PACKAGE_NAME>");
+ pw.println(" Resets the game mode of the app to device configuration.");
+ pw.println(" --mode [2|3|performance|battery]: apply for the given mode,");
+ pw.println(" resets all modes when unspecified.");
+ pw.println(" --user <USER_ID>: apply for the given user,");
+ pw.println(" the current user is used when unspecified.");
}
}
diff --git a/services/core/java/com/android/server/app/GameServiceProviderInstanceFactoryImpl.java b/services/core/java/com/android/server/app/GameServiceProviderInstanceFactoryImpl.java
index d5ac03ab7c0d..b4c43f6f1b93 100644
--- a/services/core/java/com/android/server/app/GameServiceProviderInstanceFactoryImpl.java
+++ b/services/core/java/com/android/server/app/GameServiceProviderInstanceFactoryImpl.java
@@ -20,6 +20,7 @@ import android.annotation.NonNull;
import android.app.ActivityTaskManager;
import android.content.Context;
import android.content.Intent;
+import android.os.ServiceManager;
import android.service.games.GameService;
import android.service.games.GameSessionService;
import android.service.games.IGameService;
@@ -27,6 +28,9 @@ import android.service.games.IGameSessionService;
import com.android.internal.infra.ServiceConnector;
import com.android.internal.os.BackgroundThread;
+import com.android.server.LocalServices;
+import com.android.server.wm.WindowManagerInternal;
+import com.android.server.wm.WindowManagerService;
final class GameServiceProviderInstanceFactoryImpl implements GameServiceProviderInstanceFactory {
private final Context mContext;
@@ -44,6 +48,8 @@ final class GameServiceProviderInstanceFactoryImpl implements GameServiceProvide
BackgroundThread.getExecutor(),
new GameClassifierImpl(mContext.getPackageManager()),
ActivityTaskManager.getService(),
+ (WindowManagerService) ServiceManager.getService(Context.WINDOW_SERVICE),
+ LocalServices.getService(WindowManagerInternal.class),
new GameServiceConnector(mContext, gameServiceProviderConfiguration),
new GameSessionServiceConnector(mContext, gameServiceProviderConfiguration));
}
diff --git a/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java b/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java
index cc060e94a52a..43c9839a04d8 100644
--- a/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java
+++ b/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java
@@ -17,24 +17,37 @@
package com.android.server.app;
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityManager.RunningTaskInfo;
+import android.app.ActivityTaskManager;
import android.app.IActivityTaskManager;
import android.app.TaskStackListener;
import android.content.ComponentName;
-import android.os.IBinder;
+import android.graphics.Bitmap;
+import android.graphics.Rect;
import android.os.RemoteException;
import android.os.UserHandle;
import android.service.games.CreateGameSessionRequest;
+import android.service.games.CreateGameSessionResult;
+import android.service.games.GameScreenshotResult;
+import android.service.games.GameSessionViewHostConfiguration;
import android.service.games.GameStartedEvent;
import android.service.games.IGameService;
import android.service.games.IGameServiceController;
import android.service.games.IGameSession;
+import android.service.games.IGameSessionController;
import android.service.games.IGameSessionService;
import android.util.Slog;
+import android.view.SurfaceControlViewHost.SurfacePackage;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.infra.AndroidFuture;
import com.android.internal.infra.ServiceConnector;
+import com.android.server.wm.WindowManagerInternal;
+import com.android.server.wm.WindowManagerService;
+import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
@@ -62,6 +75,19 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan
GameServiceProviderInstanceImpl.this.onTaskRemoved(taskId);
});
}
+
+ @Override
+ public void onTaskFocusChanged(int taskId, boolean focused) {
+ mBackgroundExecutor.execute(() -> {
+ GameServiceProviderInstanceImpl.this.onTaskFocusChanged(taskId, focused);
+ });
+ }
+
+ // TODO(b/204503192): Limit the lifespan of the game session in the Game Service provider
+ // to only when the associated task is running. Right now it is possible for a task to
+ // move into the background and for all associated processes to die and for the Game Session
+ // provider's GameSessionService to continue to be running. Ideally we could unbind the
+ // service when this happens.
};
private final IGameServiceController mGameServiceController =
@@ -74,11 +100,25 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan
}
};
+ private final IGameSessionController mGameSessionController =
+ new IGameSessionController.Stub() {
+ @Override
+ public void takeScreenshot(int taskId,
+ @NonNull AndroidFuture gameScreenshotResultFuture) {
+ mBackgroundExecutor.execute(() -> {
+ GameServiceProviderInstanceImpl.this.takeScreenshot(taskId,
+ gameScreenshotResultFuture);
+ });
+ }
+ };
+
private final Object mLock = new Object();
private final UserHandle mUserHandle;
private final Executor mBackgroundExecutor;
private final GameClassifier mGameClassifier;
private final IActivityTaskManager mActivityTaskManager;
+ private final WindowManagerService mWindowManagerService;
+ private final WindowManagerInternal mWindowManagerInternal;
private final ServiceConnector<IGameService> mGameServiceConnector;
private final ServiceConnector<IGameSessionService> mGameSessionServiceConnector;
@@ -89,16 +129,20 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan
private volatile boolean mIsRunning;
GameServiceProviderInstanceImpl(
- UserHandle userHandle,
+ @NonNull UserHandle userHandle,
@NonNull Executor backgroundExecutor,
@NonNull GameClassifier gameClassifier,
@NonNull IActivityTaskManager activityTaskManager,
+ @NonNull WindowManagerService windowManagerService,
+ @NonNull WindowManagerInternal windowManagerInternal,
@NonNull ServiceConnector<IGameService> gameServiceConnector,
@NonNull ServiceConnector<IGameSessionService> gameSessionServiceConnector) {
mUserHandle = userHandle;
mBackgroundExecutor = backgroundExecutor;
mGameClassifier = gameClassifier;
mActivityTaskManager = activityTaskManager;
+ mWindowManagerService = windowManagerService;
+ mWindowManagerInternal = windowManagerInternal;
mGameServiceConnector = gameServiceConnector;
mGameSessionServiceConnector = gameSessionServiceConnector;
}
@@ -151,16 +195,7 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan
}
for (GameSessionRecord gameSessionRecord : mGameSessions.values()) {
- IGameSession gameSession = gameSessionRecord.getGameSession();
- if (gameSession == null) {
- continue;
- }
-
- try {
- gameSession.destroy();
- } catch (RemoteException ex) {
- Slog.w(TAG, "Failed to destroy session: " + gameSessionRecord, ex);
- }
+ destroyGameSessionFromRecord(gameSessionRecord);
}
mGameSessions.clear();
@@ -185,31 +220,55 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan
}
}
+ private void onTaskFocusChanged(int taskId, boolean focused) {
+ synchronized (mLock) {
+ onTaskFocusChangedLocked(taskId, focused);
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void onTaskFocusChangedLocked(int taskId, boolean focused) {
+ if (DEBUG) {
+ Slog.d(TAG, "onTaskFocusChangedLocked() id: " + taskId + " focused: " + focused);
+ }
+
+ final GameSessionRecord gameSessionRecord = mGameSessions.get(taskId);
+ if (gameSessionRecord == null || gameSessionRecord.getGameSession() == null) {
+ return;
+ }
+
+ try {
+ gameSessionRecord.getGameSession().onTaskFocusChanged(focused);
+ } catch (RemoteException ex) {
+ Slog.w(TAG, "Failed to notify session of task focus change: " + gameSessionRecord);
+ }
+ }
+
@GuardedBy("mLock")
- private void gameTaskStartedLocked(int sessionId, @NonNull ComponentName componentName) {
+ private void gameTaskStartedLocked(int taskId, @NonNull ComponentName componentName) {
if (DEBUG) {
- Slog.i(TAG, "gameStartedLocked() id: " + sessionId + " component: " + componentName);
+ Slog.i(TAG, "gameStartedLocked() id: " + taskId + " component: " + componentName);
}
if (!mIsRunning) {
return;
}
- GameSessionRecord existingGameSessionRecord = mGameSessions.get(sessionId);
+ GameSessionRecord existingGameSessionRecord = mGameSessions.get(taskId);
if (existingGameSessionRecord != null) {
- Slog.w(TAG, "Existing game session found for task (id: " + sessionId
+ Slog.w(TAG, "Existing game session found for task (id: " + taskId
+ ") creation. Ignoring.");
return;
}
GameSessionRecord gameSessionRecord = GameSessionRecord.awaitingGameSessionRequest(
- sessionId, componentName);
- mGameSessions.put(sessionId, gameSessionRecord);
+ taskId, componentName);
+ mGameSessions.put(taskId, gameSessionRecord);
AndroidFuture<Void> unusedPostGameStartedFuture = mGameServiceConnector.post(
gameService -> {
gameService.gameStarted(
- new GameStartedEvent(sessionId, componentName.getPackageName()));
+ new GameStartedEvent(taskId, componentName.getPackageName()));
});
}
@@ -220,7 +279,7 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan
return;
}
- destroyGameSessionIfNecessaryLocked(taskId);
+ removeAndDestroyGameSessionIfNecessaryLocked(taskId);
}
}
@@ -231,112 +290,175 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan
}
@GuardedBy("mLock")
- private void createGameSessionLocked(int sessionId) {
+ private void createGameSessionLocked(int taskId) {
if (DEBUG) {
- Slog.i(TAG, "createGameSessionLocked() id: " + sessionId);
+ Slog.i(TAG, "createGameSessionLocked() id: " + taskId);
}
if (!mIsRunning) {
return;
}
- GameSessionRecord existingGameSessionRecord = mGameSessions.get(sessionId);
+ GameSessionRecord existingGameSessionRecord = mGameSessions.get(taskId);
if (existingGameSessionRecord == null) {
- Slog.w(TAG, "No existing game session record found for task (id: " + sessionId
+ Slog.w(TAG, "No existing game session record found for task (id: " + taskId
+ ") creation. Ignoring.");
return;
}
if (!existingGameSessionRecord.isAwaitingGameSessionRequest()) {
- Slog.w(TAG, "Existing game session for task (id: " + sessionId
+ Slog.w(TAG, "Existing game session for task (id: " + taskId
+ ") is not awaiting game session request. Ignoring.");
return;
}
- mGameSessions.put(sessionId, existingGameSessionRecord.withGameSessionRequested());
-
- ComponentName componentName = existingGameSessionRecord.getComponentName();
-
- // TODO(b/207035150): Allow the game service provider to determine if a game session
- // should be created. For now we will assume all games should have a session.
- AndroidFuture<IBinder> gameSessionFuture = new AndroidFuture<IBinder>()
- .orTimeout(CREATE_GAME_SESSION_TIMEOUT_MS, TimeUnit.MILLISECONDS)
- .whenCompleteAsync((gameSessionIBinder, exception) -> {
- IGameSession gameSession = IGameSession.Stub.asInterface(gameSessionIBinder);
- if (exception != null || gameSession == null) {
- Slog.w(TAG, "Failed to create GameSession: " + existingGameSessionRecord,
- exception);
- synchronized (mLock) {
- destroyGameSessionIfNecessaryLocked(sessionId);
- }
- return;
- }
-
- synchronized (mLock) {
- attachGameSessionLocked(sessionId, gameSession);
- }
- }, mBackgroundExecutor);
+
+ GameSessionViewHostConfiguration gameSessionViewHostConfiguration =
+ createViewHostConfigurationForTask(taskId);
+ if (gameSessionViewHostConfiguration == null) {
+ Slog.w(TAG, "Failed to create view host configuration for task (id" + taskId
+ + ") creation. Ignoring.");
+ return;
+ }
+
+ if (DEBUG) {
+ Slog.d(TAG, "Determined initial view host configuration for task (id: " + taskId + "): "
+ + gameSessionViewHostConfiguration);
+ }
+
+ mGameSessions.put(taskId, existingGameSessionRecord.withGameSessionRequested());
+
+ AndroidFuture<CreateGameSessionResult> createGameSessionResultFuture =
+ new AndroidFuture<CreateGameSessionResult>()
+ .orTimeout(CREATE_GAME_SESSION_TIMEOUT_MS, TimeUnit.MILLISECONDS)
+ .whenCompleteAsync((createGameSessionResult, exception) -> {
+ if (exception != null || createGameSessionResult == null) {
+ Slog.w(TAG, "Failed to create GameSession: "
+ + existingGameSessionRecord,
+ exception);
+ synchronized (mLock) {
+ removeAndDestroyGameSessionIfNecessaryLocked(taskId);
+ }
+ return;
+ }
+
+ synchronized (mLock) {
+ attachGameSessionLocked(taskId, createGameSessionResult);
+ }
+
+ // The TaskStackListener may have made its task focused call for the
+ // game session's task before the game session was created, so check if
+ // the task is already focused so that the game session can be notified.
+ setGameSessionFocusedIfNecessary(taskId,
+ createGameSessionResult.getGameSession());
+ }, mBackgroundExecutor);
AndroidFuture<Void> unusedPostCreateGameSessionFuture =
mGameSessionServiceConnector.post(gameService -> {
CreateGameSessionRequest createGameSessionRequest =
- new CreateGameSessionRequest(sessionId, componentName.getPackageName());
- gameService.create(createGameSessionRequest, gameSessionFuture);
+ new CreateGameSessionRequest(
+ taskId,
+ existingGameSessionRecord.getComponentName().getPackageName());
+ gameService.create(
+ mGameSessionController,
+ createGameSessionRequest,
+ gameSessionViewHostConfiguration,
+ createGameSessionResultFuture);
});
}
+ private void setGameSessionFocusedIfNecessary(int taskId, IGameSession gameSession) {
+ try {
+ final ActivityTaskManager.RootTaskInfo rootTaskInfo =
+ mActivityTaskManager.getFocusedRootTaskInfo();
+ if (rootTaskInfo != null && rootTaskInfo.taskId == taskId) {
+ gameSession.onTaskFocusChanged(true);
+ }
+ } catch (RemoteException ex) {
+ Slog.w(TAG, "Failed to set task focused for ID: " + taskId);
+ }
+ }
+
@GuardedBy("mLock")
- private void attachGameSessionLocked(int sessionId, @NonNull IGameSession gameSession) {
+ private void attachGameSessionLocked(
+ int taskId,
+ @NonNull CreateGameSessionResult createGameSessionResult) {
if (DEBUG) {
- Slog.i(TAG, "attachGameSession() id: " + sessionId);
+ Slog.d(TAG, "attachGameSession() id: " + taskId);
}
- GameSessionRecord gameSessionRecord = mGameSessions.get(sessionId);
- boolean isValidAttachRequest = true;
+ GameSessionRecord gameSessionRecord = mGameSessions.get(taskId);
+
if (gameSessionRecord == null) {
- Slog.w(TAG, "No associated game session record. Destroying id: " + sessionId);
- isValidAttachRequest = false;
+ Slog.w(TAG, "No associated game session record. Destroying id: " + taskId);
+ destroyGameSessionDuringAttach(taskId, createGameSessionResult);
+ return;
}
- if (gameSessionRecord != null && !gameSessionRecord.isGameSessionRequested()) {
- Slog.w(TAG,
- "Game session not requested for existing game session record. Destroying id: "
- + sessionId);
- isValidAttachRequest = false;
+
+ if (!gameSessionRecord.isGameSessionRequested()) {
+ destroyGameSessionDuringAttach(taskId, createGameSessionResult);
+ return;
}
- if (!isValidAttachRequest) {
- try {
- gameSession.destroy();
- } catch (RemoteException ex) {
- Slog.w(TAG, "Failed to destroy session: " + gameSessionRecord, ex);
- }
+ try {
+ mWindowManagerInternal.addTaskOverlay(
+ taskId,
+ createGameSessionResult.getSurfacePackage());
+ } catch (IllegalArgumentException ex) {
+ Slog.w(TAG, "Failed to add task overlay. Destroying id: " + taskId);
+ destroyGameSessionDuringAttach(taskId, createGameSessionResult);
return;
}
- mGameSessions.put(sessionId, gameSessionRecord.withGameSession(gameSession));
+ mGameSessions.put(taskId,
+ gameSessionRecord.withGameSession(
+ createGameSessionResult.getGameSession(),
+ createGameSessionResult.getSurfacePackage()));
+ }
+
+ private void destroyGameSessionDuringAttach(
+ int taskId,
+ CreateGameSessionResult createGameSessionResult) {
+ try {
+ createGameSessionResult.getGameSession().onDestroyed();
+ } catch (RemoteException ex) {
+ Slog.w(TAG, "Failed to destroy session: " + taskId);
+ }
}
@GuardedBy("mLock")
- private void destroyGameSessionIfNecessaryLocked(int sessionId) {
- // TODO(b/204503192): Limit the lifespan of the game session in the Game Service provider
- // to only when the associated task is running. Right now it is possible for a task to
- // move into the background and for all associated processes to die and for the Game Session
- // provider's GameSessionService to continue to be running. Ideally we could unbind the
- // service when this happens.
+ private void removeAndDestroyGameSessionIfNecessaryLocked(int taskId) {
if (DEBUG) {
- Slog.i(TAG, "destroyGameSession() id: " + sessionId);
+ Slog.d(TAG, "destroyGameSession() id: " + taskId);
}
- GameSessionRecord gameSessionRecord = mGameSessions.remove(sessionId);
+ GameSessionRecord gameSessionRecord = mGameSessions.remove(taskId);
if (gameSessionRecord == null) {
if (DEBUG) {
- Slog.w(TAG, "No game session found for id: " + sessionId);
+ Slog.w(TAG, "No game session found for id: " + taskId);
}
return;
}
+ destroyGameSessionFromRecord(gameSessionRecord);
+ }
+
+ private void destroyGameSessionFromRecord(@NonNull GameSessionRecord gameSessionRecord) {
+ SurfacePackage surfacePackage = gameSessionRecord.getSurfacePackage();
+ if (surfacePackage != null) {
+ try {
+ mWindowManagerInternal.removeTaskOverlay(
+ gameSessionRecord.getTaskId(),
+ surfacePackage);
+ } catch (IllegalArgumentException ex) {
+ Slog.i(TAG,
+ "Failed to remove task overlay. This is expected if the task is already "
+ + "destroyed: "
+ + gameSessionRecord);
+ }
+ }
IGameSession gameSession = gameSessionRecord.getGameSession();
if (gameSession != null) {
try {
- gameSession.destroy();
+ gameSession.onDestroyed();
} catch (RemoteException ex) {
Slog.w(TAG, "Failed to destroy session: " + gameSessionRecord, ex);
}
@@ -344,7 +466,7 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan
if (mGameSessions.isEmpty()) {
if (DEBUG) {
- Slog.i(TAG, "No active game sessions. Disconnecting GameSessionService");
+ Slog.d(TAG, "No active game sessions. Disconnecting GameSessionService");
}
if (mGameSessionServiceConnector != null) {
@@ -352,4 +474,62 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan
}
}
}
+
+ @Nullable
+ private GameSessionViewHostConfiguration createViewHostConfigurationForTask(int taskId) {
+ RunningTaskInfo runningTaskInfo = getRunningTaskInfoForTask(taskId);
+ if (runningTaskInfo == null) {
+ return null;
+ }
+
+ Rect bounds = runningTaskInfo.configuration.windowConfiguration.getBounds();
+ return new GameSessionViewHostConfiguration(
+ runningTaskInfo.displayId,
+ bounds.width(),
+ bounds.height());
+ }
+
+ @Nullable
+ private RunningTaskInfo getRunningTaskInfoForTask(int taskId) {
+ List<RunningTaskInfo> runningTaskInfos;
+ try {
+ runningTaskInfos = mActivityTaskManager.getTasks(
+ /* maxNum= */ Integer.MAX_VALUE,
+ /* filterOnlyVisibleRecents= */ true,
+ /* keepIntentExtra= */ false);
+ } catch (RemoteException ex) {
+ Slog.w(TAG, "Failed to fetch running tasks");
+ return null;
+ }
+
+ for (RunningTaskInfo taskInfo : runningTaskInfos) {
+ if (taskInfo.taskId == taskId) {
+ return taskInfo;
+ }
+ }
+
+ return null;
+ }
+
+ @VisibleForTesting
+ void takeScreenshot(int taskId, @NonNull AndroidFuture callback) {
+ synchronized (mLock) {
+ boolean isTaskAssociatedWithGameSession = mGameSessions.containsKey(taskId);
+ if (!isTaskAssociatedWithGameSession) {
+ Slog.w(TAG, "No game session found for id: " + taskId);
+ callback.complete(GameScreenshotResult.createInternalErrorResult());
+ return;
+ }
+ }
+
+ mBackgroundExecutor.execute(() -> {
+ final Bitmap bitmap = mWindowManagerService.captureTaskBitmap(taskId);
+ if (bitmap == null) {
+ Slog.w(TAG, "Could not get bitmap for id: " + taskId);
+ callback.complete(GameScreenshotResult.createInternalErrorResult());
+ } else {
+ callback.complete(GameScreenshotResult.createSuccessResult(bitmap));
+ }
+ });
+ }
}
diff --git a/services/core/java/com/android/server/app/GameSessionRecord.java b/services/core/java/com/android/server/app/GameSessionRecord.java
index e9daceb0cd39..a241812f7868 100644
--- a/services/core/java/com/android/server/app/GameSessionRecord.java
+++ b/services/core/java/com/android/server/app/GameSessionRecord.java
@@ -20,6 +20,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentName;
import android.service.games.IGameSession;
+import android.view.SurfaceControlViewHost.SurfacePackage;
import java.util.Objects;
@@ -37,26 +38,34 @@ final class GameSessionRecord {
}
private final int mTaskId;
+ private final State mState;
private final ComponentName mRootComponentName;
@Nullable
private final IGameSession mIGameSession;
- private final State mState;
+ @Nullable
+ private final SurfacePackage mSurfacePackage;
static GameSessionRecord awaitingGameSessionRequest(int taskId,
ComponentName rootComponentName) {
- return new GameSessionRecord(taskId, rootComponentName, /* gameSession= */ null,
- State.NO_GAME_SESSION_REQUESTED);
+ return new GameSessionRecord(
+ taskId,
+ State.NO_GAME_SESSION_REQUESTED,
+ rootComponentName,
+ /* gameSession= */ null,
+ /* surfacePackage= */ null);
}
private GameSessionRecord(
int taskId,
+ @NonNull State state,
@NonNull ComponentName rootComponentName,
@Nullable IGameSession gameSession,
- @NonNull State state) {
+ @Nullable SurfacePackage surfacePackage) {
this.mTaskId = taskId;
+ this.mState = state;
this.mRootComponentName = rootComponentName;
this.mIGameSession = gameSession;
- this.mState = state;
+ this.mSurfacePackage = surfacePackage;
}
public boolean isAwaitingGameSessionRequest() {
@@ -65,8 +74,12 @@ final class GameSessionRecord {
@NonNull
public GameSessionRecord withGameSessionRequested() {
- return new GameSessionRecord(mTaskId, mRootComponentName, /* gameSession=*/ null,
- State.GAME_SESSION_REQUESTED);
+ return new GameSessionRecord(
+ mTaskId,
+ State.GAME_SESSION_REQUESTED,
+ mRootComponentName,
+ /* gameSession=*/ null,
+ /* surfacePackage=*/ null);
}
public boolean isGameSessionRequested() {
@@ -74,15 +87,20 @@ final class GameSessionRecord {
}
@NonNull
- public GameSessionRecord withGameSession(@NonNull IGameSession gameSession) {
+ public GameSessionRecord withGameSession(
+ @NonNull IGameSession gameSession,
+ @NonNull SurfacePackage surfacePackage) {
Objects.requireNonNull(gameSession);
- return new GameSessionRecord(mTaskId, mRootComponentName, gameSession,
- State.GAME_SESSION_ATTACHED);
+ return new GameSessionRecord(mTaskId,
+ State.GAME_SESSION_ATTACHED,
+ mRootComponentName,
+ gameSession,
+ surfacePackage);
}
- @Nullable
- public IGameSession getGameSession() {
- return mIGameSession;
+ @NonNull
+ public int getTaskId() {
+ return mTaskId;
}
@NonNull
@@ -90,17 +108,29 @@ final class GameSessionRecord {
return mRootComponentName;
}
+ @Nullable
+ public IGameSession getGameSession() {
+ return mIGameSession;
+ }
+
+ @Nullable
+ public SurfacePackage getSurfacePackage() {
+ return mSurfacePackage;
+ }
+
@Override
public String toString() {
return "GameSessionRecord{"
+ "mTaskId="
+ mTaskId
+ + ", mState="
+ + mState
+ ", mRootComponentName="
+ mRootComponentName
+ ", mIGameSession="
+ mIGameSession
- + ", mState="
- + mState
+ + ", mSurfacePackage="
+ + mSurfacePackage
+ '}';
}
@@ -115,12 +145,16 @@ final class GameSessionRecord {
}
GameSessionRecord that = (GameSessionRecord) o;
- return mTaskId == that.mTaskId && mRootComponentName.equals(that.mRootComponentName)
- && Objects.equals(mIGameSession, that.mIGameSession) && mState == that.mState;
+ return mTaskId == that.mTaskId
+ && mState == that.mState
+ && mRootComponentName.equals(that.mRootComponentName)
+ && Objects.equals(mIGameSession, that.mIGameSession)
+ && Objects.equals(mSurfacePackage, that.mSurfacePackage);
}
@Override
public int hashCode() {
- return Objects.hash(mTaskId, mRootComponentName, mIGameSession, mState);
+ return Objects.hash(
+ mTaskId, mState, mRootComponentName, mIGameSession, mState, mSurfacePackage);
}
}
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 0961fcb31ace..2dd6bf575579 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -1360,6 +1360,9 @@ public class AudioDeviceInventory {
case AudioSystem.DEVICE_OUT_USB_HEADSET:
connType = AudioRoutesInfo.MAIN_USB;
break;
+ case AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET:
+ connType = AudioRoutesInfo.MAIN_DOCK_SPEAKERS;
+ break;
}
synchronized (mCurAudioRoutes) {
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 57ae36ed906b..0ea936e35a37 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -4384,12 +4384,6 @@ public class AudioService extends IAudioService.Stub
if (!mHasVibrator) {
return false;
}
- final boolean hapticsDisabled = Settings.System.getIntForUser(mContext.getContentResolver(),
- Settings.System.HAPTIC_FEEDBACK_ENABLED, 0, UserHandle.USER_CURRENT) == 0;
- if (hapticsDisabled) {
- return false;
- }
-
if (effect == null) {
return false;
}
diff --git a/services/core/java/com/android/server/biometrics/log/BiometricFrameworkStatsLogger.java b/services/core/java/com/android/server/biometrics/log/BiometricFrameworkStatsLogger.java
new file mode 100644
index 000000000000..c3471bd1d771
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/log/BiometricFrameworkStatsLogger.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.log;
+
+import android.hardware.biometrics.BiometricsProtoEnums;
+import android.util.Slog;
+
+import com.android.internal.util.FrameworkStatsLog;
+
+/**
+ * Wrapper for {@link FrameworkStatsLog} to isolate the testable parts.
+ */
+public class BiometricFrameworkStatsLogger {
+
+ private static final String TAG = "BiometricFrameworkStatsLogger";
+
+ private static final BiometricFrameworkStatsLogger sInstance =
+ new BiometricFrameworkStatsLogger();
+
+ private BiometricFrameworkStatsLogger() {}
+
+ public static BiometricFrameworkStatsLogger getInstance() {
+ return sInstance;
+ }
+
+ /** {@see FrameworkStatsLog.BIOMETRIC_ACQUIRED}. */
+ public void acquired(
+ int statsModality, int statsAction, int statsClient, boolean isDebug,
+ int acquiredInfo, int vendorCode, boolean isCrypto, int targetUserId) {
+ FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_ACQUIRED,
+ statsModality,
+ targetUserId,
+ isCrypto,
+ statsAction,
+ statsClient,
+ acquiredInfo,
+ vendorCode,
+ isDebug,
+ -1 /* sensorId */);
+ }
+
+ /** {@see FrameworkStatsLog.BIOMETRIC_AUTHENTICATED}. */
+ public void authenticate(
+ int statsModality, int statsAction, int statsClient, boolean isDebug, long latency,
+ boolean authenticated, int authState, boolean requireConfirmation, boolean isCrypto,
+ int targetUserId, boolean isBiometricPrompt, float ambientLightLux) {
+ FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_AUTHENTICATED,
+ statsModality,
+ targetUserId,
+ isCrypto,
+ statsClient,
+ requireConfirmation,
+ authState,
+ sanitizeLatency(latency),
+ isDebug,
+ -1 /* sensorId */,
+ ambientLightLux);
+ }
+
+ /** {@see FrameworkStatsLog.BIOMETRIC_ENROLLED}. */
+ public void enroll(int statsModality, int statsAction, int statsClient,
+ int targetUserId, long latency, boolean enrollSuccessful, float ambientLightLux) {
+ FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_ENROLLED,
+ statsModality,
+ targetUserId,
+ sanitizeLatency(latency),
+ enrollSuccessful,
+ -1, /* sensorId */
+ ambientLightLux);
+ }
+
+ /** {@see FrameworkStatsLog.BIOMETRIC_ERROR_OCCURRED}. */
+ public void error(
+ int statsModality, int statsAction, int statsClient, boolean isDebug, long latency,
+ int error, int vendorCode, boolean isCrypto, int targetUserId) {
+ FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_ERROR_OCCURRED,
+ statsModality,
+ targetUserId,
+ isCrypto,
+ statsAction,
+ statsClient,
+ error,
+ vendorCode,
+ isDebug,
+ sanitizeLatency(latency),
+ -1 /* sensorId */);
+ }
+
+ /** {@see FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED}. */
+ public void reportUnknownTemplateEnrolledHal(int statsModality) {
+ FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
+ statsModality,
+ BiometricsProtoEnums.ISSUE_UNKNOWN_TEMPLATE_ENROLLED_HAL,
+ -1 /* sensorId */);
+ }
+
+ /** {@see FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED}. */
+ public void reportUnknownTemplateEnrolledFramework(int statsModality) {
+ FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
+ statsModality,
+ BiometricsProtoEnums.ISSUE_UNKNOWN_TEMPLATE_ENROLLED_FRAMEWORK,
+ -1 /* sensorId */);
+ }
+
+ private long sanitizeLatency(long latency) {
+ if (latency < 0) {
+ Slog.w(TAG, "found a negative latency : " + latency);
+ return -1;
+ }
+ return latency;
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/LoggableMonitor.java b/services/core/java/com/android/server/biometrics/log/BiometricLogger.java
index b4c82f2ed799..d029af38c683 100644
--- a/services/core/java/com/android/server/biometrics/sensors/LoggableMonitor.java
+++ b/services/core/java/com/android/server/biometrics/log/BiometricLogger.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.biometrics.sensors;
+package com.android.server.biometrics.log;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -29,71 +29,28 @@ import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
import android.util.Slog;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.biometrics.Utils;
/**
- * Abstract class that adds logging functionality to the ClientMonitor classes.
+ * Logger for all reported Biometric framework events.
*/
-public abstract class LoggableMonitor {
+public class BiometricLogger {
- public static final String TAG = "Biometrics/LoggableMonitor";
+ public static final String TAG = "BiometricLogger";
public static final boolean DEBUG = false;
- final int mStatsModality;
+ private final int mStatsModality;
private final int mStatsAction;
private final int mStatsClient;
+ private final BiometricFrameworkStatsLogger mSink;
@NonNull private final SensorManager mSensorManager;
+
private long mFirstAcquireTimeMs;
private boolean mLightSensorEnabled = false;
private boolean mShouldLogMetrics = true;
- /**
- * Probe for loggable attributes that can be continuously monitored, such as ambient light.
- *
- * Disable probes when the sensors are in states that are not interesting for monitoring
- * purposes to save power.
- */
- protected interface Probe {
- /** Ensure the probe is actively sampling for new data. */
- void enable();
- /** Stop sampling data. */
- void disable();
- }
-
- /**
- * Client monitor callback that exposes a probe.
- *
- * Disables the probe when the operation completes.
- */
- protected static class CallbackWithProbe<T extends Probe>
- implements BaseClientMonitor.Callback {
- private final boolean mStartWithClient;
- private final T mProbe;
-
- public CallbackWithProbe(@NonNull T probe, boolean startWithClient) {
- mProbe = probe;
- mStartWithClient = startWithClient;
- }
-
- @Override
- public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
- if (mStartWithClient) {
- mProbe.enable();
- }
- }
-
- @Override
- public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean success) {
- mProbe.disable();
- }
-
- @NonNull
- public T getProbe() {
- return mProbe;
- }
- }
-
private class ALSProbe implements Probe {
@Override
public void enable() {
@@ -128,26 +85,30 @@ public abstract class LoggableMonitor {
* @param statsAction One of {@link BiometricsProtoEnums} ACTION_* constants.
* @param statsClient One of {@link BiometricsProtoEnums} CLIENT_* constants.
*/
- public LoggableMonitor(@NonNull Context context, int statsModality, int statsAction,
- int statsClient) {
+ public BiometricLogger(
+ @NonNull Context context, int statsModality, int statsAction, int statsClient) {
+ this(statsModality, statsAction, statsClient,
+ BiometricFrameworkStatsLogger.getInstance(),
+ context.getSystemService(SensorManager.class));
+ }
+
+ @VisibleForTesting
+ BiometricLogger(
+ int statsModality, int statsAction, int statsClient,
+ BiometricFrameworkStatsLogger logSink, SensorManager sensorManager) {
mStatsModality = statsModality;
mStatsAction = statsAction;
mStatsClient = statsClient;
- mSensorManager = context.getSystemService(SensorManager.class);
+ mSink = logSink;
+ mSensorManager = sensorManager;
}
- /**
- * Only valid for AuthenticationClient.
- * @return true if the client is authenticating for a crypto operation.
- */
- protected boolean isCryptoOperation() {
- return false;
- }
-
- protected void setShouldLog(boolean shouldLog) {
- mShouldLogMetrics = shouldLog;
+ /** Disable logging metrics and only log critical events, such as system health issues. */
+ public void disableMetrics() {
+ mShouldLogMetrics = false;
}
+ /** {@link BiometricsProtoEnums} CLIENT_* constants */
public int getStatsClient() {
return mStatsClient;
}
@@ -171,8 +132,9 @@ public abstract class LoggableMonitor {
return shouldSkipLogging;
}
- protected final void logOnAcquired(Context context, int acquiredInfo, int vendorCode,
- int targetUserId) {
+ /** Log an acquisition event. */
+ public void logOnAcquired(Context context,
+ int acquiredInfo, int vendorCode, boolean isCrypto, int targetUserId) {
if (!mShouldLogMetrics) {
return;
}
@@ -192,7 +154,7 @@ public abstract class LoggableMonitor {
if (DEBUG) {
Slog.v(TAG, "Acquired! Modality: " + mStatsModality
+ ", User: " + targetUserId
- + ", IsCrypto: " + isCryptoOperation()
+ + ", IsCrypto: " + isCrypto
+ ", Action: " + mStatsAction
+ ", Client: " + mStatsClient
+ ", AcquiredInfo: " + acquiredInfo
@@ -203,19 +165,14 @@ public abstract class LoggableMonitor {
return;
}
- FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_ACQUIRED,
- mStatsModality,
- targetUserId,
- isCryptoOperation(),
- mStatsAction,
- mStatsClient,
- acquiredInfo,
- vendorCode,
+ mSink.acquired(mStatsModality, mStatsAction, mStatsClient,
Utils.isDebugEnabled(context, targetUserId),
- -1 /* sensorId */);
+ acquiredInfo, vendorCode, isCrypto, targetUserId);
}
- protected final void logOnError(Context context, int error, int vendorCode, int targetUserId) {
+ /** Log an error during an operation. */
+ public void logOnError(Context context,
+ int error, int vendorCode, boolean isCrypto, int targetUserId) {
if (!mShouldLogMetrics) {
return;
}
@@ -226,7 +183,7 @@ public abstract class LoggableMonitor {
if (DEBUG) {
Slog.v(TAG, "Error! Modality: " + mStatsModality
+ ", User: " + targetUserId
- + ", IsCrypto: " + isCryptoOperation()
+ + ", IsCrypto: " + isCrypto
+ ", Action: " + mStatsAction
+ ", Client: " + mStatsClient
+ ", Error: " + error
@@ -240,21 +197,15 @@ public abstract class LoggableMonitor {
return;
}
- FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_ERROR_OCCURRED,
- mStatsModality,
- targetUserId,
- isCryptoOperation(),
- mStatsAction,
- mStatsClient,
- error,
- vendorCode,
- Utils.isDebugEnabled(context, targetUserId),
- sanitizeLatency(latency),
- -1 /* sensorId */);
+ mSink.error(mStatsModality, mStatsAction, mStatsClient,
+ Utils.isDebugEnabled(context, targetUserId), latency,
+ error, vendorCode, isCrypto, targetUserId);
}
- protected final void logOnAuthenticated(Context context, boolean authenticated,
- boolean requireConfirmation, int targetUserId, boolean isBiometricPrompt) {
+ /** Log authentication attempt. */
+ public void logOnAuthenticated(Context context,
+ boolean authenticated, boolean requireConfirmation, boolean isCrypto,
+ int targetUserId, boolean isBiometricPrompt) {
if (!mShouldLogMetrics) {
return;
}
@@ -279,7 +230,7 @@ public abstract class LoggableMonitor {
if (DEBUG) {
Slog.v(TAG, "Authenticated! Modality: " + mStatsModality
+ ", User: " + targetUserId
- + ", IsCrypto: " + isCryptoOperation()
+ + ", IsCrypto: " + isCrypto
+ ", Client: " + mStatsClient
+ ", RequireConfirmation: " + requireConfirmation
+ ", State: " + authState
@@ -293,20 +244,14 @@ public abstract class LoggableMonitor {
return;
}
- FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_AUTHENTICATED,
- mStatsModality,
- targetUserId,
- isCryptoOperation(),
- mStatsClient,
- requireConfirmation,
- authState,
- sanitizeLatency(latency),
+ mSink.authenticate(mStatsModality, mStatsAction, mStatsClient,
Utils.isDebugEnabled(context, targetUserId),
- -1 /* sensorId */,
- mLastAmbientLux /* ambientLightLux */);
+ latency, authenticated, authState, requireConfirmation, isCrypto,
+ targetUserId, isBiometricPrompt, mLastAmbientLux);
}
- protected final void logOnEnrolled(int targetUserId, long latency, boolean enrollSuccessful) {
+ /** Log enrollment outcome. */
+ public void logOnEnrolled(int targetUserId, long latency, boolean enrollSuccessful) {
if (!mShouldLogMetrics) {
return;
}
@@ -326,25 +271,30 @@ public abstract class LoggableMonitor {
return;
}
- FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_ENROLLED,
- mStatsModality,
- targetUserId,
- sanitizeLatency(latency),
- enrollSuccessful,
- -1, /* sensorId */
- mLastAmbientLux /* ambientLightLux */);
+ mSink.enroll(mStatsModality, mStatsAction, mStatsClient,
+ targetUserId, latency, enrollSuccessful, mLastAmbientLux);
}
- private long sanitizeLatency(long latency) {
- if (latency < 0) {
- Slog.w(TAG, "found a negative latency : " + latency);
- return -1;
+ /** Report unexpected enrollment reported by the HAL. */
+ public void logUnknownEnrollmentInHal() {
+ if (shouldSkipLogging()) {
+ return;
}
- return latency;
+
+ mSink.reportUnknownTemplateEnrolledHal(mStatsModality);
+ }
+
+ /** Report unknown enrollment in framework settings */
+ public void logUnknownEnrollmentInFramework() {
+ if (shouldSkipLogging()) {
+ return;
+ }
+
+ mSink.reportUnknownTemplateEnrolledFramework(mStatsModality);
}
/**
- * Get a callback to start/stop ALS capture when client runs.
+ * Get a callback to start/stop ALS capture when a client runs.
*
* If the probe should not run for the entire operation, do not set startWithClient and
* start/stop the problem when needed.
@@ -352,7 +302,7 @@ public abstract class LoggableMonitor {
* @param startWithClient if probe should start automatically when the operation starts.
*/
@NonNull
- protected CallbackWithProbe<Probe> createALSCallback(boolean startWithClient) {
+ public CallbackWithProbe<Probe> createALSCallback(boolean startWithClient) {
return new CallbackWithProbe<>(new ALSProbe(), startWithClient);
}
diff --git a/services/core/java/com/android/server/biometrics/log/CallbackWithProbe.java b/services/core/java/com/android/server/biometrics/log/CallbackWithProbe.java
new file mode 100644
index 000000000000..f7b736885f71
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/log/CallbackWithProbe.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.log;
+
+import android.annotation.NonNull;
+
+import com.android.server.biometrics.sensors.BaseClientMonitor;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
+
+/**
+ * Client monitor callback that exposes a probe.
+ *
+ * Disables the probe when the operation completes.
+ *
+ * @param <T> probe type
+ */
+public class CallbackWithProbe<T extends Probe> implements ClientMonitorCallback {
+ private final boolean mStartWithClient;
+ private final T mProbe;
+
+ public CallbackWithProbe(@NonNull T probe, boolean startWithClient) {
+ mProbe = probe;
+ mStartWithClient = startWithClient;
+ }
+
+ @Override
+ public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
+ if (mStartWithClient) {
+ mProbe.enable();
+ }
+ }
+
+ @Override
+ public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean success) {
+ mProbe.disable();
+ }
+
+ @NonNull
+ public T getProbe() {
+ return mProbe;
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/log/Probe.java b/services/core/java/com/android/server/biometrics/log/Probe.java
new file mode 100644
index 000000000000..9e6fc6b8b8b2
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/log/Probe.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.log;
+
+/**
+ * Probe for loggable attributes that can be continuously monitored, such as ambient light.
+ *
+ * Disable probes when the sensors are in states that are not interesting for monitoring
+ * purposes to save power.
+ */
+public interface Probe {
+ /** Ensure the probe is actively sampling for new data. */
+ void enable();
+ /** Stop sampling data. */
+ void disable();
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
index 6f7176816ddb..86d72ba1c06f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
@@ -105,7 +105,8 @@ public abstract class AcquisitionClient<T> extends HalClientMonitor<T> implement
// that do not handle lockout under the HAL. In these cases, ensure that the framework only
// sends errors once per ClientMonitor.
if (mShouldSendErrorToClient) {
- logOnError(getContext(), errorCode, vendorCode, getTargetUserId());
+ getLogger().logOnError(getContext(), errorCode, vendorCode,
+ isCryptoOperation(), getTargetUserId());
try {
if (getListener() != null) {
mShouldSendErrorToClient = false;
@@ -137,7 +138,7 @@ public abstract class AcquisitionClient<T> extends HalClientMonitor<T> implement
}
@Override
- public void cancelWithoutStarting(@NonNull Callback callback) {
+ public void cancelWithoutStarting(@NonNull ClientMonitorCallback callback) {
Slog.d(TAG, "cancelWithoutStarting: " + this);
final int errorCode = BiometricConstants.BIOMETRIC_ERROR_CANCELED;
@@ -163,7 +164,8 @@ public abstract class AcquisitionClient<T> extends HalClientMonitor<T> implement
protected final void onAcquiredInternal(int acquiredInfo, int vendorCode,
boolean shouldSend) {
- super.logOnAcquired(getContext(), acquiredInfo, vendorCode, getTargetUserId());
+ getLogger().logOnAcquired(getContext(), acquiredInfo, vendorCode,
+ isCryptoOperation(), getTargetUserId());
if (DEBUG) {
Slog.v(TAG, "Acquired: " + acquiredInfo + " " + vendorCode
+ ", shouldSend: " + shouldSend);
diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
index 358263df916b..35a0f575ec97 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
@@ -91,7 +91,7 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T>
/**
* Handles lifecycle, e.g. {@link BiometricScheduler},
- * {@link com.android.server.biometrics.sensors.BaseClientMonitor.Callback} after authentication
+ * {@link ClientMonitorCallback} after authentication
* results are known. Note that this happens asynchronously from (but shortly after)
* {@link #onAuthenticated(BiometricAuthenticator.Identifier, boolean, ArrayList)} and allows
* {@link CoexCoordinator} a chance to invoke/delay this event.
@@ -180,8 +180,8 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T>
@Override
public void onAuthenticated(BiometricAuthenticator.Identifier identifier,
boolean authenticated, ArrayList<Byte> hardwareAuthToken) {
- super.logOnAuthenticated(getContext(), authenticated, mRequireConfirmation,
- getTargetUserId(), isBiometricPrompt());
+ getLogger().logOnAuthenticated(getContext(), authenticated, mRequireConfirmation,
+ isCryptoOperation(), getTargetUserId(), isBiometricPrompt());
final ClientMonitorCallbackConverter listener = getListener();
@@ -440,7 +440,7 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T>
* Start authentication
*/
@Override
- public void start(@NonNull Callback callback) {
+ public void start(@NonNull ClientMonitorCallback callback) {
super.start(callback);
final @LockoutTracker.LockoutMode int lockoutMode =
diff --git a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
index 26bbb403f39f..e1f7e2ab5461 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
@@ -27,18 +27,16 @@ import android.os.RemoteException;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.biometrics.log.BiometricLogger;
-import java.util.ArrayList;
-import java.util.List;
import java.util.NoSuchElementException;
/**
- * Abstract base class for keeping track and dispatching events from the biometric's HAL to the
+ * Abstract base class for keeping track and dispatching events from the biometric's HAL to
* the current client. Subclasses are responsible for coordinating the interaction with
* the biometric's HAL for the specific action (e.g. authenticate, enroll, enumerate, etc.).
*/
-public abstract class BaseClientMonitor extends LoggableMonitor
- implements IBinder.DeathRecipient {
+public abstract class BaseClientMonitor implements IBinder.DeathRecipient {
private static final String TAG = "Biometrics/ClientMonitor";
protected static final boolean DEBUG = true;
@@ -46,68 +44,12 @@ public abstract class BaseClientMonitor extends LoggableMonitor
// Counter used to distinguish between ClientMonitor instances to help debugging.
private static int sCount = 0;
- /**
- * Interface that ClientMonitor holders should use to receive callbacks.
- */
- public interface Callback {
- /**
- * Invoked when the ClientMonitor operation has been started (e.g. reached the head of
- * the queue and becomes the current operation).
- *
- * @param clientMonitor Reference of the ClientMonitor that is starting.
- */
- default void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
- }
-
- /**
- * Invoked when the ClientMonitor operation is complete. This abstracts away asynchronous
- * (i.e. Authenticate, Enroll, Enumerate, Remove) and synchronous (i.e. generateChallenge,
- * revokeChallenge) so that a scheduler can process ClientMonitors regardless of their
- * implementation.
- *
- * @param clientMonitor Reference of the ClientMonitor that finished.
- * @param success True if the operation completed successfully.
- */
- default void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean success) {
- }
- }
-
- /** Holder for wrapping multiple handlers into a single Callback. */
- public static class CompositeCallback implements Callback {
- @NonNull
- private final List<Callback> mCallbacks;
-
- public CompositeCallback(@NonNull Callback... callbacks) {
- mCallbacks = new ArrayList<>();
-
- for (Callback callback : callbacks) {
- if (callback != null) {
- mCallbacks.add(callback);
- }
- }
- }
-
- @Override
- public final void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
- for (int i = 0; i < mCallbacks.size(); i++) {
- mCallbacks.get(i).onClientStarted(clientMonitor);
- }
- }
-
- @Override
- public final void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
- boolean success) {
- for (int i = mCallbacks.size() - 1; i >= 0; i--) {
- mCallbacks.get(i).onClientFinished(clientMonitor, success);
- }
- }
- }
-
private final int mSequentialId;
@NonNull private final Context mContext;
private final int mTargetUserId;
@NonNull private final String mOwner;
private final int mSensorId; // sensorId as configured by the framework
+ @NonNull private final BiometricLogger mLogger;
@Nullable private IBinder mToken;
private long mRequestId;
@@ -119,7 +61,7 @@ public abstract class BaseClientMonitor extends LoggableMonitor
// Use an empty callback by default since delayed operations can receive events
// before they are started and cause NPE in subclasses that access this field directly.
- @NonNull protected Callback mCallback = new Callback() {
+ @NonNull protected ClientMonitorCallback mCallback = new ClientMonitorCallback() {
@Override
public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
Slog.e(TAG, "mCallback onClientStarted: called before set (should not happen)");
@@ -133,18 +75,6 @@ public abstract class BaseClientMonitor extends LoggableMonitor
};
/**
- * @return A ClientMonitorEnum constant defined in biometrics.proto
- */
- public abstract int getProtoEnum();
-
- /**
- * @return True if the ClientMonitor should cancel any current and pending interruptable clients
- */
- public boolean interruptsPrecedingClients() {
- return false;
- }
-
- /**
* @param context system_server context
* @param token a unique token for the client
* @param listener recipient of related events (e.g. authentication)
@@ -160,7 +90,14 @@ public abstract class BaseClientMonitor extends LoggableMonitor
@Nullable IBinder token, @Nullable ClientMonitorCallbackConverter listener, int userId,
@NonNull String owner, int cookie, int sensorId, int statsModality, int statsAction,
int statsClient) {
- super(context, statsModality, statsAction, statsClient);
+ this(context, token, listener, userId, owner, cookie, sensorId,
+ new BiometricLogger(context, statsModality, statsAction, statsClient));
+ }
+
+ @VisibleForTesting
+ BaseClientMonitor(@NonNull Context context,
+ @Nullable IBinder token, @Nullable ClientMonitorCallbackConverter listener, int userId,
+ @NonNull String owner, int cookie, int sensorId, @NonNull BiometricLogger logger) {
mSequentialId = sCount++;
mContext = context;
mToken = token;
@@ -170,6 +107,7 @@ public abstract class BaseClientMonitor extends LoggableMonitor
mOwner = owner;
mCookie = cookie;
mSensorId = sensorId;
+ mLogger = logger;
try {
if (token != null) {
@@ -180,15 +118,19 @@ public abstract class BaseClientMonitor extends LoggableMonitor
}
}
- public int getCookie() {
- return mCookie;
+ /** A ClientMonitorEnum constant defined in biometrics.proto */
+ public abstract int getProtoEnum();
+
+ /** True if the ClientMonitor should cancel any current and pending interruptable clients. */
+ public boolean interruptsPrecedingClients() {
+ return false;
}
/**
* Starts the ClientMonitor's lifecycle.
* @param callback invoked when the operation is complete (succeeds, fails, etc)
*/
- public void start(@NonNull Callback callback) {
+ public void start(@NonNull ClientMonitorCallback callback) {
mCallback = wrapCallbackForStart(callback);
mCallback.onClientStarted(this);
}
@@ -199,7 +141,7 @@ public abstract class BaseClientMonitor extends LoggableMonitor
* Returns the original callback unless overridden.
*/
@NonNull
- protected Callback wrapCallbackForStart(@NonNull Callback callback) {
+ protected ClientMonitorCallback wrapCallbackForStart(@NonNull ClientMonitorCallback callback) {
return callback;
}
@@ -257,6 +199,20 @@ public abstract class BaseClientMonitor extends LoggableMonitor
}
}
+ /**
+ * Only valid for AuthenticationClient.
+ * @return true if the client is authenticating for a crypto operation.
+ */
+ protected boolean isCryptoOperation() {
+ return false;
+ }
+
+ /** Logger for this client */
+ @NonNull
+ public BiometricLogger getLogger() {
+ return mLogger;
+ }
+
public final Context getContext() {
return mContext;
}
@@ -281,6 +237,11 @@ public abstract class BaseClientMonitor extends LoggableMonitor
return mSensorId;
}
+ /** Cookie set when this monitor was created. */
+ public int getCookie() {
+ return mCookie;
+ }
+
/** Unique request id. */
public final long getRequestId() {
return mRequestId;
@@ -305,7 +266,7 @@ public abstract class BaseClientMonitor extends LoggableMonitor
}
@VisibleForTesting
- public Callback getCallback() {
+ public ClientMonitorCallback getCallback() {
return mCallback;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
index 39c5944d65c7..1a6da94f683e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
@@ -160,7 +160,7 @@ public class BiometricScheduler {
// Internal callback, notified when an operation is complete. Notifies the requester
// that the operation is complete, before performing internal scheduler work (such as
// starting the next client).
- private final BaseClientMonitor.Callback mInternalCallback = new BaseClientMonitor.Callback() {
+ private final ClientMonitorCallback mInternalCallback = new ClientMonitorCallback() {
@Override
public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
Slog.d(getTag(), "[Started] " + clientMonitor);
@@ -247,7 +247,7 @@ public class BiometricScheduler {
}
@VisibleForTesting
- public BaseClientMonitor.Callback getInternalCallback() {
+ public ClientMonitorCallback getInternalCallback() {
return mInternalCallback;
}
@@ -368,7 +368,7 @@ public class BiometricScheduler {
* @param clientCallback optional callback, invoked when the client state changes.
*/
public void scheduleClientMonitor(@NonNull BaseClientMonitor clientMonitor,
- @Nullable BaseClientMonitor.Callback clientCallback) {
+ @Nullable ClientMonitorCallback clientCallback) {
// If the incoming operation should interrupt preceding clients, mark any interruptable
// pending clients as canceling. Once they reach the head of the queue, the scheduler will
// send ERROR_CANCELED and skip the operation.
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java b/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java
index e8b50d90b586..812ca8ac62fe 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java
@@ -65,7 +65,7 @@ public class BiometricSchedulerOperation {
protected static final int STATE_WAITING_FOR_COOKIE = 4;
/**
- * The {@link BaseClientMonitor.Callback} has been invoked and the client is finished.
+ * The {@link ClientMonitorCallback} has been invoked and the client is finished.
*/
protected static final int STATE_FINISHED = 5;
@@ -83,7 +83,7 @@ public class BiometricSchedulerOperation {
@NonNull
private final BaseClientMonitor mClientMonitor;
@Nullable
- private final BaseClientMonitor.Callback mClientCallback;
+ private final ClientMonitorCallback mClientCallback;
@OperationState
private int mState;
@VisibleForTesting
@@ -92,14 +92,14 @@ public class BiometricSchedulerOperation {
BiometricSchedulerOperation(
@NonNull BaseClientMonitor clientMonitor,
- @Nullable BaseClientMonitor.Callback callback
+ @Nullable ClientMonitorCallback callback
) {
this(clientMonitor, callback, STATE_WAITING_IN_QUEUE);
}
protected BiometricSchedulerOperation(
@NonNull BaseClientMonitor clientMonitor,
- @Nullable BaseClientMonitor.Callback callback,
+ @Nullable ClientMonitorCallback callback,
@OperationState int state
) {
mClientMonitor = clientMonitor;
@@ -139,7 +139,7 @@ public class BiometricSchedulerOperation {
* @param callback lifecycle callback
* @return if this operation started
*/
- public boolean start(@NonNull BaseClientMonitor.Callback callback) {
+ public boolean start(@NonNull ClientMonitorCallback callback) {
checkInState("start",
STATE_WAITING_IN_QUEUE,
STATE_WAITING_FOR_COOKIE,
@@ -159,7 +159,7 @@ public class BiometricSchedulerOperation {
* @param cookie cookie indicting the operation should begin
* @return if this operation started
*/
- public boolean startWithCookie(@NonNull BaseClientMonitor.Callback callback, int cookie) {
+ public boolean startWithCookie(@NonNull ClientMonitorCallback callback, int cookie) {
checkInState("start",
STATE_WAITING_IN_QUEUE,
STATE_WAITING_FOR_COOKIE,
@@ -173,8 +173,8 @@ public class BiometricSchedulerOperation {
return doStart(callback);
}
- private boolean doStart(@NonNull BaseClientMonitor.Callback callback) {
- final BaseClientMonitor.Callback cb = getWrappedCallback(callback);
+ private boolean doStart(@NonNull ClientMonitorCallback callback) {
+ final ClientMonitorCallback cb = getWrappedCallback(callback);
if (mState == STATE_WAITING_IN_QUEUE_CANCELING) {
Slog.d(TAG, "Operation marked for cancellation, cancelling now: " + this);
@@ -239,9 +239,9 @@ public class BiometricSchedulerOperation {
*
* @param handler handler to use for the cancellation watchdog
* @param callback lifecycle callback (only used if this operation hasn't started, otherwise
- * the callback used from {@link #start(BaseClientMonitor.Callback)} is used)
+ * the callback used from {@link #start(ClientMonitorCallback)} is used)
*/
- public void cancel(@NonNull Handler handler, @NonNull BaseClientMonitor.Callback callback) {
+ public void cancel(@NonNull Handler handler, @NonNull ClientMonitorCallback callback) {
checkNotInState("cancel", STATE_FINISHED);
final int currentState = mState;
@@ -270,14 +270,14 @@ public class BiometricSchedulerOperation {
}
@NonNull
- private BaseClientMonitor.Callback getWrappedCallback() {
+ private ClientMonitorCallback getWrappedCallback() {
return getWrappedCallback(null);
}
@NonNull
- private BaseClientMonitor.Callback getWrappedCallback(
- @Nullable BaseClientMonitor.Callback callback) {
- final BaseClientMonitor.Callback destroyCallback = new BaseClientMonitor.Callback() {
+ private ClientMonitorCallback getWrappedCallback(
+ @Nullable ClientMonitorCallback callback) {
+ final ClientMonitorCallback destroyCallback = new ClientMonitorCallback() {
@Override
public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
boolean success) {
@@ -286,7 +286,7 @@ public class BiometricSchedulerOperation {
mState = STATE_FINISHED;
}
};
- return new BaseClientMonitor.CompositeCallback(destroyCallback, callback, mClientCallback);
+ return new ClientMonitorCompositeCallback(destroyCallback, callback, mClientCallback);
}
/** {@link BaseClientMonitor#getSensorId()}. */
diff --git a/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallback.java b/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallback.java
new file mode 100644
index 000000000000..8ea4ee911cb1
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallback.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors;
+
+import android.annotation.NonNull;
+
+/**
+ * Interface that ClientMonitor holders should use to receive callbacks.
+ */
+public interface ClientMonitorCallback {
+ /**
+ * Invoked when the ClientMonitor operation has been started (e.g. reached the head of
+ * the queue and becomes the current operation).
+ *
+ * @param clientMonitor Reference of the ClientMonitor that is starting.
+ */
+ default void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {}
+
+ /**
+ * Invoked when the ClientMonitor operation is complete. This abstracts away asynchronous
+ * (i.e. Authenticate, Enroll, Enumerate, Remove) and synchronous (i.e. generateChallenge,
+ * revokeChallenge) so that a scheduler can process ClientMonitors regardless of their
+ * implementation.
+ *
+ * @param clientMonitor Reference of the ClientMonitor that finished.
+ * @param success True if the operation completed successfully.
+ */
+ default void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean success) {}
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCompositeCallback.java b/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCompositeCallback.java
new file mode 100644
index 000000000000..b82f5fa129d6
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCompositeCallback.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors;
+
+import android.annotation.NonNull;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** Holder for wrapping multiple handlers into a single Callback. */
+public class ClientMonitorCompositeCallback implements ClientMonitorCallback {
+ @NonNull
+ private final List<ClientMonitorCallback> mCallbacks;
+
+ public ClientMonitorCompositeCallback(@NonNull ClientMonitorCallback... callbacks) {
+ mCallbacks = new ArrayList<>();
+
+ for (ClientMonitorCallback callback : callbacks) {
+ if (callback != null) {
+ mCallbacks.add(callback);
+ }
+ }
+ }
+
+ @Override
+ public final void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ mCallbacks.get(i).onClientStarted(clientMonitor);
+ }
+ }
+
+ @Override
+ public final void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
+ boolean success) {
+ for (int i = mCallbacks.size() - 1; i >= 0; i--) {
+ mCallbacks.get(i).onClientFinished(clientMonitor, success);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
index 2826e0c97305..3b7adc1a6176 100644
--- a/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
@@ -89,7 +89,8 @@ public abstract class EnrollClient<T> extends AcquisitionClient<T> implements En
if (remaining == 0) {
mBiometricUtils.addBiometricForUser(getContext(), getTargetUserId(), identifier);
- logOnEnrolled(getTargetUserId(), System.currentTimeMillis() - mEnrollmentStartTimeMs,
+ getLogger().logOnEnrolled(getTargetUserId(),
+ System.currentTimeMillis() - mEnrollmentStartTimeMs,
true /* enrollSuccessful */);
mCallback.onClientFinished(this, true /* success */);
}
@@ -97,7 +98,7 @@ public abstract class EnrollClient<T> extends AcquisitionClient<T> implements En
}
@Override
- public void start(@NonNull Callback callback) {
+ public void start(@NonNull ClientMonitorCallback callback) {
super.start(callback);
if (hasReachedEnrollmentLimit()) {
@@ -116,7 +117,8 @@ public abstract class EnrollClient<T> extends AcquisitionClient<T> implements En
*/
@Override
public void onError(int error, int vendorCode) {
- logOnEnrolled(getTargetUserId(), System.currentTimeMillis() - mEnrollmentStartTimeMs,
+ getLogger().logOnEnrolled(getTargetUserId(),
+ System.currentTimeMillis() - mEnrollmentStartTimeMs,
false /* enrollSuccessful */);
super.onError(error, vendorCode);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/EnrollmentModifier.java b/services/core/java/com/android/server/biometrics/sensors/EnrollmentModifier.java
index c2f909b08bb6..3060f30bc084 100644
--- a/services/core/java/com/android/server/biometrics/sensors/EnrollmentModifier.java
+++ b/services/core/java/com/android/server/biometrics/sensors/EnrollmentModifier.java
@@ -23,7 +23,7 @@ public interface EnrollmentModifier {
/**
* Callers should typically check this after
- * {@link BaseClientMonitor.Callback#onClientFinished(BaseClientMonitor, boolean)}
+ * {@link ClientMonitorCallback#onClientFinished(BaseClientMonitor, boolean)}
*
* @return true if the user has gone from:
* 1) none-enrolled --> enrolled
diff --git a/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java
index 3d74f369efde..6fb6d08cd602 100644
--- a/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java
@@ -47,7 +47,7 @@ public abstract class GenerateChallengeClient<T> extends HalClientMonitor<T> {
}
@Override
- public void start(@NonNull Callback callback) {
+ public void start(@NonNull ClientMonitorCallback callback) {
super.start(callback);
startHalOperation();
diff --git a/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java
index 63cd4125262d..c8830f8049a2 100644
--- a/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java
@@ -45,7 +45,7 @@ public abstract class HalClientMonitor<T> extends BaseClientMonitor {
/**
* Invoked if the scheduler is unable to start the ClientMonitor (for example the HAL is null).
* If such a problem is detected, the scheduler will not invoke
- * {@link #start(Callback)}.
+ * {@link #start(ClientMonitorCallback)}.
*/
public abstract void unableToStart();
diff --git a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
index 579dfd69ec66..0636893eabf7 100644
--- a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
@@ -23,7 +23,6 @@ import android.hardware.biometrics.BiometricsProtoEnums;
import android.os.IBinder;
import android.util.Slog;
-import com.android.internal.util.FrameworkStatsLog;
import com.android.server.biometrics.BiometricsProto;
import java.util.ArrayList;
@@ -65,7 +64,7 @@ public abstract class InternalCleanupClient<S extends BiometricAuthenticator.Ide
private final boolean mHasEnrollmentsBeforeStarting;
private BaseClientMonitor mCurrentTask;
- private final Callback mEnumerateCallback = new Callback() {
+ private final ClientMonitorCallback mEnumerateCallback = new ClientMonitorCallback() {
@Override
public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean success) {
final List<BiometricAuthenticator.Identifier> unknownHALTemplates =
@@ -91,7 +90,7 @@ public abstract class InternalCleanupClient<S extends BiometricAuthenticator.Ide
}
};
- private final Callback mRemoveCallback = new Callback() {
+ private final ClientMonitorCallback mRemoveCallback = new ClientMonitorCallback() {
@Override
public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean success) {
Slog.d(TAG, "Remove onClientFinished: " + clientMonitor + ", success: " + success);
@@ -128,10 +127,9 @@ public abstract class InternalCleanupClient<S extends BiometricAuthenticator.Ide
mCurrentTask = getRemovalClient(getContext(), mLazyDaemon, getToken(),
template.mIdentifier.getBiometricId(), template.mUserId,
getContext().getPackageName(), mBiometricUtils, getSensorId(), mAuthenticatorIds);
- FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
- mStatsModality,
- BiometricsProtoEnums.ISSUE_UNKNOWN_TEMPLATE_ENROLLED_HAL,
- -1 /* sensorId */);
+
+ getLogger().logUnknownEnrollmentInHal();
+
mCurrentTask.start(mRemoveCallback);
}
@@ -141,7 +139,7 @@ public abstract class InternalCleanupClient<S extends BiometricAuthenticator.Ide
}
@Override
- public void start(@NonNull Callback callback) {
+ public void start(@NonNull ClientMonitorCallback callback) {
super.start(callback);
// Start enumeration. Removal will start if necessary, when enumeration is completed.
diff --git a/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java
index 7f6903a17b32..05ea19a3aa14 100644
--- a/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java
@@ -23,7 +23,6 @@ import android.hardware.biometrics.BiometricsProtoEnums;
import android.os.IBinder;
import android.util.Slog;
-import com.android.internal.util.FrameworkStatsLog;
import com.android.server.biometrics.BiometricsProto;
import java.util.ArrayList;
@@ -73,7 +72,7 @@ public abstract class InternalEnumerateClient<T> extends HalClientMonitor<T>
}
@Override
- public void start(@NonNull Callback callback) {
+ public void start(@NonNull ClientMonitorCallback callback) {
super.start(callback);
// The biometric template ids will be removed when we get confirmation from the HAL
@@ -116,10 +115,8 @@ public abstract class InternalEnumerateClient<T> extends HalClientMonitor<T>
+ identifier.getBiometricId() + " " + identifier.getName());
mUtils.removeBiometricForUser(getContext(),
getTargetUserId(), identifier.getBiometricId());
- FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
- mStatsModality,
- BiometricsProtoEnums.ISSUE_UNKNOWN_TEMPLATE_ENROLLED_FRAMEWORK,
- -1 /* sensorId */);
+
+ getLogger().logUnknownEnrollmentInFramework();
}
mEnrolledList.clear();
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/Interruptable.java b/services/core/java/com/android/server/biometrics/sensors/Interruptable.java
index d5093c756415..4f645efcccf0 100644
--- a/services/core/java/com/android/server/biometrics/sensors/Interruptable.java
+++ b/services/core/java/com/android/server/biometrics/sensors/Interruptable.java
@@ -29,15 +29,15 @@ public interface Interruptable {
/**
* Notifies the client that it needs to finish before
- * {@link BaseClientMonitor#start(BaseClientMonitor.Callback)} was invoked. This usually happens
+ * {@link BaseClientMonitor#start(ClientMonitorCallback)} was invoked. This usually happens
* if the client is still waiting in the pending queue and got notified that a subsequent
* operation is preempting it.
*
* This method must invoke
- * {@link BaseClientMonitor.Callback#onClientFinished(BaseClientMonitor, boolean)} on the
+ * {@link ClientMonitorCallback#onClientFinished(BaseClientMonitor, boolean)} on the
* given callback (with success).
*
* @param callback invoked when the operation is completed.
*/
- void cancelWithoutStarting(@NonNull BaseClientMonitor.Callback callback);
+ void cancelWithoutStarting(@NonNull ClientMonitorCallback callback);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/InvalidationClient.java b/services/core/java/com/android/server/biometrics/sensors/InvalidationClient.java
index cede4a725246..ee6bb0f0886a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/InvalidationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/InvalidationClient.java
@@ -62,7 +62,7 @@ public abstract class InvalidationClient<S extends BiometricAuthenticator.Identi
}
@Override
- public void start(@NonNull Callback callback) {
+ public void start(@NonNull ClientMonitorCallback callback) {
super.start(callback);
startHalOperation();
diff --git a/services/core/java/com/android/server/biometrics/sensors/InvalidationRequesterClient.java b/services/core/java/com/android/server/biometrics/sensors/InvalidationRequesterClient.java
index 5ba1b0000a7c..b2661a28012d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/InvalidationRequesterClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/InvalidationRequesterClient.java
@@ -84,7 +84,7 @@ public class InvalidationRequesterClient<S extends BiometricAuthenticator.Identi
}
@Override
- public void start(@NonNull Callback callback) {
+ public void start(@NonNull ClientMonitorCallback callback) {
super.start(callback);
mUtils.setInvalidationInProgress(getContext(), getTargetUserId(), true /* inProgress */);
diff --git a/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java
index 2a6677e55d60..e79819b401ea 100644
--- a/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java
@@ -17,7 +17,6 @@
package com.android.server.biometrics.sensors;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.content.Context;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricsProtoEnums;
@@ -59,7 +58,7 @@ public abstract class RemovalClient<S extends BiometricAuthenticator.Identifier,
}
@Override
- public void start(@NonNull Callback callback) {
+ public void start(@NonNull ClientMonitorCallback callback) {
super.start(callback);
// The biometric template ids will be removed when we get confirmation from the HAL
diff --git a/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java
index 1edf5afef3b2..21a6ddfcde66 100644
--- a/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java
@@ -38,7 +38,7 @@ public abstract class RevokeChallengeClient<T> extends HalClientMonitor<T> {
}
@Override
- public void start(@NonNull Callback callback) {
+ public void start(@NonNull ClientMonitorCallback callback) {
super.start(callback);
startHalOperation();
diff --git a/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java
index 603cc22968a9..4f900208841e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java
+++ b/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java
@@ -56,7 +56,7 @@ public class UserAwareBiometricScheduler extends BiometricScheduler {
@NonNull private final UserSwitchCallback mUserSwitchCallback;
@Nullable private StopUserClient<?> mStopUserClient;
- private class ClientFinishedCallback implements BaseClientMonitor.Callback {
+ private class ClientFinishedCallback implements ClientMonitorCallback {
private final BaseClientMonitor mOwner;
ClientFinishedCallback(BaseClientMonitor owner) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java
index 77e431c81192..1e9b72b6f4d5 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java
@@ -29,7 +29,7 @@ import android.os.IBinder;
import android.util.proto.ProtoOutputStream;
import android.view.Surface;
-import com.android.server.biometrics.sensors.BaseClientMonitor;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.LockoutTracker;
@@ -137,7 +137,7 @@ public interface ServiceProvider {
void startPreparedClient(int sensorId, int cookie);
void scheduleInternalCleanup(int sensorId, int userId,
- @Nullable BaseClientMonitor.Callback callback);
+ @Nullable ClientMonitorCallback callback);
void dumpProtoState(int sensorId, @NonNull ProtoOutputStream proto,
boolean clearSchedulerBuffer);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java
index 66b942b085a4..89982691ae38 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java
@@ -35,6 +35,7 @@ import android.util.Slog;
import com.android.server.biometrics.HardwareAuthTokenUtils;
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.sensors.BaseClientMonitor;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.face.FaceUtils;
import java.util.HashSet;
@@ -221,7 +222,7 @@ public class BiometricTestSessionImpl extends ITestSession.Stub {
Utils.checkPermission(mContext, TEST_BIOMETRIC);
Slog.d(TAG, "cleanupInternalState: " + userId);
- mProvider.scheduleInternalCleanup(mSensorId, userId, new BaseClientMonitor.Callback() {
+ mProvider.scheduleInternalCleanup(mSensorId, userId, new ClientMonitorCallback() {
@Override
public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
try {
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
index 4131ae127ab2..dc21a04fb446 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
@@ -39,7 +39,9 @@ import com.android.internal.R;
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.sensors.AuthenticationClient;
import com.android.server.biometrics.sensors.BiometricNotificationUtils;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+import com.android.server.biometrics.sensors.ClientMonitorCompositeCallback;
import com.android.server.biometrics.sensors.LockoutCache;
import com.android.server.biometrics.sensors.LockoutConsumer;
import com.android.server.biometrics.sensors.LockoutTracker;
@@ -97,15 +99,16 @@ class FaceAuthenticationClient extends AuthenticationClient<ISession> implements
}
@Override
- public void start(@NonNull Callback callback) {
+ public void start(@NonNull ClientMonitorCallback callback) {
super.start(callback);
mState = STATE_STARTED;
}
@NonNull
@Override
- protected Callback wrapCallbackForStart(@NonNull Callback callback) {
- return new CompositeCallback(createALSCallback(true /* startWithClient */), callback);
+ protected ClientMonitorCallback wrapCallbackForStart(@NonNull ClientMonitorCallback callback) {
+ return new ClientMonitorCompositeCallback(
+ getLogger().createALSCallback(true /* startWithClient */), callback);
}
@Override
@@ -241,7 +244,8 @@ class FaceAuthenticationClient extends AuthenticationClient<ISession> implements
mLockoutCache.setLockoutModeForUser(getTargetUserId(), LockoutTracker.LOCKOUT_TIMED);
// Lockout metrics are logged as an error code.
final int error = BiometricFaceConstants.FACE_ERROR_LOCKOUT;
- logOnError(getContext(), error, 0 /* vendorCode */, getTargetUserId());
+ getLogger().logOnError(getContext(), error, 0 /* vendorCode */,
+ isCryptoOperation(), getTargetUserId());
try {
getListener().onError(getSensorId(), getCookie(), error, 0 /* vendorCode */);
@@ -256,7 +260,8 @@ class FaceAuthenticationClient extends AuthenticationClient<ISession> implements
mLockoutCache.setLockoutModeForUser(getTargetUserId(), LockoutTracker.LOCKOUT_PERMANENT);
// Lockout metrics are logged as an error code.
final int error = BiometricFaceConstants.FACE_ERROR_LOCKOUT_PERMANENT;
- logOnError(getContext(), error, 0 /* vendorCode */, getTargetUserId());
+ getLogger().logOnError(getContext(), error, 0 /* vendorCode */,
+ isCryptoOperation(), getTargetUserId());
try {
getListener().onError(getSensorId(), getCookie(), error, 0 /* vendorCode */);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java
index 2158dfe7bde5..72a20db077dd 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java
@@ -30,6 +30,7 @@ import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
import com.android.server.biometrics.sensors.AcquisitionClient;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.DetectionConsumer;
@@ -58,7 +59,7 @@ public class FaceDetectClient extends AcquisitionClient<ISession> implements Det
}
@Override
- public void start(@NonNull Callback callback) {
+ public void start(@NonNull ClientMonitorCallback callback) {
super.start(callback);
startHalOperation();
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
index aae4fbe9b0d7..5c57dbbffedb 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
@@ -41,7 +41,9 @@ import com.android.server.biometrics.Utils;
import com.android.server.biometrics.sensors.BaseClientMonitor;
import com.android.server.biometrics.sensors.BiometricNotificationUtils;
import com.android.server.biometrics.sensors.BiometricUtils;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+import com.android.server.biometrics.sensors.ClientMonitorCompositeCallback;
import com.android.server.biometrics.sensors.EnrollClient;
import com.android.server.biometrics.sensors.face.FaceService;
import com.android.server.biometrics.sensors.face.FaceUtils;
@@ -67,8 +69,8 @@ public class FaceEnrollClient extends EnrollClient<ISession> {
private final int mMaxTemplatesPerUser;
private final boolean mDebugConsent;
- private final BaseClientMonitor.Callback mPreviewHandleDeleterCallback =
- new BaseClientMonitor.Callback() {
+ private final ClientMonitorCallback mPreviewHandleDeleterCallback =
+ new ClientMonitorCallback() {
@Override
public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
}
@@ -101,7 +103,7 @@ public class FaceEnrollClient extends EnrollClient<ISession> {
}
@Override
- public void start(@NonNull Callback callback) {
+ public void start(@NonNull ClientMonitorCallback callback) {
super.start(callback);
BiometricNotificationUtils.cancelReEnrollNotification(getContext());
@@ -109,9 +111,9 @@ public class FaceEnrollClient extends EnrollClient<ISession> {
@NonNull
@Override
- protected Callback wrapCallbackForStart(@NonNull Callback callback) {
- return new CompositeCallback(mPreviewHandleDeleterCallback,
- createALSCallback(true /* startWithClient */), callback);
+ protected ClientMonitorCallback wrapCallbackForStart(@NonNull ClientMonitorCallback callback) {
+ return new ClientMonitorCompositeCallback(mPreviewHandleDeleterCallback,
+ getLogger().createALSCallback(true /* startWithClient */), callback);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java
index af826c2f68ba..584b58cdd7c7 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java
@@ -24,6 +24,7 @@ import android.os.RemoteException;
import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.HalClientMonitor;
import java.util.Map;
@@ -48,7 +49,7 @@ class FaceGetAuthenticatorIdClient extends HalClientMonitor<ISession> {
// Nothing to do here
}
- public void start(@NonNull Callback callback) {
+ public void start(@NonNull ClientMonitorCallback callback) {
super.start(callback);
startHalOperation();
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetFeatureClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetFeatureClient.java
index 315ede8bc5df..acf5720cd0cf 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetFeatureClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetFeatureClient.java
@@ -29,6 +29,7 @@ import android.provider.Settings;
import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.ErrorConsumer;
import com.android.server.biometrics.sensors.HalClientMonitor;
@@ -60,7 +61,7 @@ public class FaceGetFeatureClient extends HalClientMonitor<ISession> implements
}
@Override
- public void start(@NonNull Callback callback) {
+ public void start(@NonNull ClientMonitorCallback callback) {
super.start(callback);
startHalOperation();
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
index ae507abea537..9d7a5529f473 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
@@ -49,6 +49,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.sensors.AuthenticationClient;
import com.android.server.biometrics.sensors.BaseClientMonitor;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.InvalidationRequesterClient;
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
@@ -217,7 +218,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
}
private void scheduleForSensor(int sensorId, @NonNull BaseClientMonitor client,
- BaseClientMonitor.Callback callback) {
+ ClientMonitorCallback callback) {
if (!mSensors.contains(sensorId)) {
throw new IllegalStateException("Unable to schedule client: " + client
+ " for sensor: " + sensorId);
@@ -341,7 +342,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
opPackageName, id, FaceUtils.getInstance(sensorId), disabledFeatures,
ENROLL_TIMEOUT_SEC, previewSurface, sensorId, maxTemplatesPerUser,
debugConsent);
- scheduleForSensor(sensorId, client, new BaseClientMonitor.Callback() {
+ scheduleForSensor(sensorId, client, new ClientMonitorCallback() {
@Override
public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
boolean success) {
@@ -511,7 +512,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
@Override
public void scheduleInternalCleanup(int sensorId, int userId,
- @Nullable BaseClientMonitor.Callback callback) {
+ @Nullable ClientMonitorCallback callback) {
mHandler.post(() -> {
final List<Face> enrolledList = getEnrolledFaces(sensorId, userId);
final FaceInternalCleanupClient client =
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java
index 1e1b532961df..fd44c5cf4afc 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java
@@ -27,6 +27,7 @@ import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
import com.android.server.biometrics.HardwareAuthTokenUtils;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ErrorConsumer;
import com.android.server.biometrics.sensors.HalClientMonitor;
import com.android.server.biometrics.sensors.LockoutCache;
@@ -64,7 +65,7 @@ public class FaceResetLockoutClient extends HalClientMonitor<ISession> implement
}
@Override
- public void start(@NonNull Callback callback) {
+ public void start(@NonNull ClientMonitorCallback callback) {
super.start(callback);
startHalOperation();
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceSetFeatureClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceSetFeatureClient.java
index 4515d0421a58..ee6982abd9ed 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceSetFeatureClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceSetFeatureClient.java
@@ -28,6 +28,7 @@ import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
import com.android.server.biometrics.HardwareAuthTokenUtils;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.ErrorConsumer;
import com.android.server.biometrics.sensors.HalClientMonitor;
@@ -65,7 +66,7 @@ public class FaceSetFeatureClient extends HalClientMonitor<ISession> implements
}
@Override
- public void start(@NonNull Callback callback) {
+ public void start(@NonNull ClientMonitorCallback callback) {
super.start(callback);
startHalOperation();
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStartUserClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStartUserClient.java
index 2b5f49546d69..4a3da0d929dc 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStartUserClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStartUserClient.java
@@ -27,6 +27,7 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.StartUserClient;
public class FaceStartUserClient extends StartUserClient<IFace, ISession> {
@@ -43,7 +44,7 @@ public class FaceStartUserClient extends StartUserClient<IFace, ISession> {
}
@Override
- public void start(@NonNull Callback callback) {
+ public void start(@NonNull ClientMonitorCallback callback) {
super.start(callback);
startHalOperation();
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStopUserClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStopUserClient.java
index 06328e311b06..88b92359268c 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStopUserClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStopUserClient.java
@@ -24,6 +24,7 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.StopUserClient;
public class FaceStopUserClient extends StopUserClient<ISession> {
@@ -36,7 +37,7 @@ public class FaceStopUserClient extends StopUserClient<ISession> {
}
@Override
- public void start(@NonNull Callback callback) {
+ public void start(@NonNull ClientMonitorCallback callback) {
super.start(callback);
startHalOperation();
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java
index b45578b4d447..e7483b3eeae3 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java
@@ -32,6 +32,7 @@ import android.util.Slog;
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.sensors.BaseClientMonitor;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.face.FaceUtils;
import java.util.ArrayList;
@@ -197,7 +198,7 @@ public class BiometricTestSessionImpl extends ITestSession.Stub {
public void cleanupInternalState(int userId) {
Utils.checkPermission(mContext, TEST_BIOMETRIC);
- mFace10.scheduleInternalCleanup(mSensorId, userId, new BaseClientMonitor.Callback() {
+ mFace10.scheduleInternalCleanup(mSensorId, userId, new ClientMonitorCallback() {
@Override
public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
try {
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
index e957794372aa..9a52db19ecda 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
@@ -60,6 +60,7 @@ import com.android.server.biometrics.sensors.AuthenticationConsumer;
import com.android.server.biometrics.sensors.BaseClientMonitor;
import com.android.server.biometrics.sensors.BiometricNotificationUtils;
import com.android.server.biometrics.sensors.BiometricScheduler;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.EnumerateConsumer;
import com.android.server.biometrics.sensors.ErrorConsumer;
@@ -534,7 +535,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId,
opPackageName, mSensorId, sSystemClock.millis());
mGeneratedChallengeCache = client;
- mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() {
+ mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
@Override
public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
if (client != clientMonitor) {
@@ -562,7 +563,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
final FaceRevokeChallengeClient client = new FaceRevokeChallengeClient(mContext,
mLazyDaemon, token, userId, opPackageName, mSensorId);
- mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() {
+ mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
@Override
public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
boolean success) {
@@ -591,7 +592,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
opPackageName, id, FaceUtils.getLegacyInstance(mSensorId), disabledFeatures,
ENROLL_TIMEOUT_SEC, previewSurface, mSensorId);
- mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() {
+ mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
@Override
public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
boolean success) {
@@ -742,7 +743,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
final int faceId = faces.get(0).getBiometricId();
final FaceGetFeatureClient client = new FaceGetFeatureClient(mContext, mLazyDaemon,
token, listener, userId, opPackageName, mSensorId, feature, faceId);
- mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() {
+ mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
@Override
public void onClientFinished(
@NonNull BaseClientMonitor clientMonitor, boolean success) {
@@ -760,7 +761,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
}
private void scheduleInternalCleanup(int userId,
- @Nullable BaseClientMonitor.Callback callback) {
+ @Nullable ClientMonitorCallback callback) {
mHandler.post(() -> {
scheduleUpdateActiveUserWithoutHandler(userId);
@@ -774,7 +775,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
@Override
public void scheduleInternalCleanup(int sensorId, int userId,
- @Nullable BaseClientMonitor.Callback callback) {
+ @Nullable ClientMonitorCallback callback) {
scheduleInternalCleanup(userId, callback);
}
@@ -890,7 +891,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
final FaceUpdateActiveUserClient client = new FaceUpdateActiveUserClient(mContext,
mLazyDaemon, targetUserId, mContext.getOpPackageName(), mSensorId,
hasEnrolled, mAuthenticatorIds);
- mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() {
+ mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
@Override
public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
boolean success) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java
index 7548d2871a15..1e0e7992bf87 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java
@@ -34,7 +34,9 @@ import com.android.internal.R;
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.sensors.AuthenticationClient;
import com.android.server.biometrics.sensors.BiometricNotificationUtils;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+import com.android.server.biometrics.sensors.ClientMonitorCompositeCallback;
import com.android.server.biometrics.sensors.LockoutTracker;
import com.android.server.biometrics.sensors.face.UsageStats;
@@ -87,15 +89,16 @@ class FaceAuthenticationClient extends AuthenticationClient<IBiometricsFace> {
}
@Override
- public void start(@NonNull Callback callback) {
+ public void start(@NonNull ClientMonitorCallback callback) {
super.start(callback);
mState = STATE_STARTED;
}
@NonNull
@Override
- protected Callback wrapCallbackForStart(@NonNull Callback callback) {
- return new CompositeCallback(createALSCallback(true /* startWithClient */), callback);
+ protected ClientMonitorCallback wrapCallbackForStart(@NonNull ClientMonitorCallback callback) {
+ return new ClientMonitorCompositeCallback(
+ getLogger().createALSCallback(true /* startWithClient */), callback);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java
index 31e5c86103fb..8068e14cf0f0 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java
@@ -33,7 +33,9 @@ import android.view.Surface;
import com.android.internal.R;
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.sensors.BiometricUtils;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+import com.android.server.biometrics.sensors.ClientMonitorCompositeCallback;
import com.android.server.biometrics.sensors.EnrollClient;
import java.util.ArrayList;
@@ -69,8 +71,9 @@ public class FaceEnrollClient extends EnrollClient<IBiometricsFace> {
@NonNull
@Override
- protected Callback wrapCallbackForStart(@NonNull Callback callback) {
- return new CompositeCallback(createALSCallback(true /* startWithClient */), callback);
+ protected ClientMonitorCallback wrapCallbackForStart(@NonNull ClientMonitorCallback callback) {
+ return new ClientMonitorCompositeCallback(
+ getLogger().createALSCallback(true /* startWithClient */), callback);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClient.java
index f418104834e3..e29a1923e47e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClient.java
@@ -25,6 +25,7 @@ import android.os.RemoteException;
import android.util.Slog;
import com.android.internal.util.Preconditions;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.GenerateChallengeClient;
@@ -39,7 +40,7 @@ public class FaceGenerateChallengeClient extends GenerateChallengeClient<IBiomet
private static final String TAG = "FaceGenerateChallengeClient";
static final int CHALLENGE_TIMEOUT_SEC = 600; // 10 minutes
- private static final Callback EMPTY_CALLBACK = new Callback() {
+ private static final ClientMonitorCallback EMPTY_CALLBACK = new ClientMonitorCallback() {
};
private final long mCreatedAt;
@@ -94,7 +95,7 @@ public class FaceGenerateChallengeClient extends GenerateChallengeClient<IBiomet
}
private void sendChallengeResult(@NonNull ClientMonitorCallbackConverter receiver,
- @NonNull Callback ownerCallback) {
+ @NonNull ClientMonitorCallback ownerCallback) {
Preconditions.checkState(mChallengeResult != null, "result not available");
try {
receiver.onChallengeGenerated(getSensorId(), getTargetUserId(), mChallengeResult);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGetFeatureClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGetFeatureClient.java
index 7821601c8433..0a9d96d00eb6 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGetFeatureClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGetFeatureClient.java
@@ -28,6 +28,7 @@ import android.os.RemoteException;
import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.HalClientMonitor;
@@ -66,7 +67,7 @@ public class FaceGetFeatureClient extends HalClientMonitor<IBiometricsFace> {
}
@Override
- public void start(@NonNull Callback callback) {
+ public void start(@NonNull ClientMonitorCallback callback) {
super.start(callback);
startHalOperation();
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceResetLockoutClient.java
index 9d977d60e705..ee01c43a2e73 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceResetLockoutClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceResetLockoutClient.java
@@ -24,6 +24,7 @@ import android.os.RemoteException;
import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.HalClientMonitor;
import java.util.ArrayList;
@@ -57,7 +58,7 @@ public class FaceResetLockoutClient extends HalClientMonitor<IBiometricsFace> {
}
@Override
- public void start(@NonNull Callback callback) {
+ public void start(@NonNull ClientMonitorCallback callback) {
super.start(callback);
startHalOperation();
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceSetFeatureClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceSetFeatureClient.java
index cc3d8f0e28ba..ee28f7b0f304 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceSetFeatureClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceSetFeatureClient.java
@@ -26,6 +26,7 @@ import android.os.RemoteException;
import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.HalClientMonitor;
@@ -71,7 +72,7 @@ public class FaceSetFeatureClient extends HalClientMonitor<IBiometricsFace> {
}
@Override
- public void start(@NonNull Callback callback) {
+ public void start(@NonNull ClientMonitorCallback callback) {
super.start(callback);
startHalOperation();
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceUpdateActiveUserClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceUpdateActiveUserClient.java
index 5343d0d17273..8ee8ce522035 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceUpdateActiveUserClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceUpdateActiveUserClient.java
@@ -25,6 +25,7 @@ import android.os.RemoteException;
import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.HalClientMonitor;
import java.io.File;
@@ -49,7 +50,7 @@ public class FaceUpdateActiveUserClient extends HalClientMonitor<IBiometricsFace
}
@Override
- public void start(@NonNull Callback callback) {
+ public void start(@NonNull ClientMonitorCallback callback) {
super.start(callback);
startHalOperation();
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintStateCallback.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintStateCallback.java
index be0e6edb2a42..04fd534adb3b 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintStateCallback.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintStateCallback.java
@@ -31,6 +31,7 @@ import android.util.Slog;
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.sensors.AuthenticationClient;
import com.android.server.biometrics.sensors.BaseClientMonitor;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.EnrollClient;
import com.android.server.biometrics.sensors.EnrollmentModifier;
@@ -39,7 +40,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
/**
* A callback for receiving notifications about changes in fingerprint state.
*/
-public class FingerprintStateCallback implements BaseClientMonitor.Callback {
+public class FingerprintStateCallback implements ClientMonitorCallback {
@NonNull private final CopyOnWriteArrayList<IFingerprintStateListener>
mFingerprintStateListeners = new CopyOnWriteArrayList<>();
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
index 535705c63cab..0bdc4ebad66e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
@@ -30,7 +30,7 @@ import android.hardware.fingerprint.IUdfpsOverlayController;
import android.os.IBinder;
import android.util.proto.ProtoOutputStream;
-import com.android.server.biometrics.sensors.BaseClientMonitor;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.LockoutTracker;
@@ -121,7 +121,7 @@ public interface ServiceProvider {
@NonNull String opPackageName);
void scheduleInternalCleanup(int sensorId, int userId,
- @Nullable BaseClientMonitor.Callback callback);
+ @Nullable ClientMonitorCallback callback);
boolean isHardwareDetected(int sensorId);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java
index 2b50b96c69a1..b29fbb66fa50 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java
@@ -32,6 +32,7 @@ import android.util.Slog;
import com.android.server.biometrics.HardwareAuthTokenUtils;
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.sensors.BaseClientMonitor;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.fingerprint.FingerprintStateCallback;
import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
@@ -204,7 +205,7 @@ class BiometricTestSessionImpl extends ITestSession.Stub {
Utils.checkPermission(mContext, TEST_BIOMETRIC);
Slog.d(TAG, "cleanupInternalState: " + userId);
- mProvider.scheduleInternalCleanup(mSensorId, userId, new BaseClientMonitor.Callback() {
+ mProvider.scheduleInternalCleanup(mSensorId, userId, new ClientMonitorCallback() {
@Override
public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
try {
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
index e4d5fba3a471..f3d0121c98eb 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
@@ -33,9 +33,13 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
+import com.android.server.biometrics.log.CallbackWithProbe;
+import com.android.server.biometrics.log.Probe;
import com.android.server.biometrics.sensors.AuthenticationClient;
import com.android.server.biometrics.sensors.BiometricNotificationUtils;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+import com.android.server.biometrics.sensors.ClientMonitorCompositeCallback;
import com.android.server.biometrics.sensors.LockoutCache;
import com.android.server.biometrics.sensors.LockoutConsumer;
import com.android.server.biometrics.sensors.LockoutTracker;
@@ -80,11 +84,11 @@ class FingerprintAuthenticationClient extends AuthenticationClient<ISession> imp
mLockoutCache = lockoutCache;
mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController);
mSensorProps = sensorProps;
- mALSProbeCallback = createALSCallback(false /* startWithClient */);
+ mALSProbeCallback = getLogger().createALSCallback(false /* startWithClient */);
}
@Override
- public void start(@NonNull Callback callback) {
+ public void start(@NonNull ClientMonitorCallback callback) {
super.start(callback);
if (mSensorProps.isAnyUdfpsType()) {
@@ -97,8 +101,8 @@ class FingerprintAuthenticationClient extends AuthenticationClient<ISession> imp
@NonNull
@Override
- protected Callback wrapCallbackForStart(@NonNull Callback callback) {
- return new CompositeCallback(mALSProbeCallback, callback);
+ protected ClientMonitorCallback wrapCallbackForStart(@NonNull ClientMonitorCallback callback) {
+ return new ClientMonitorCompositeCallback(mALSProbeCallback, callback);
}
@Override
@@ -233,7 +237,8 @@ class FingerprintAuthenticationClient extends AuthenticationClient<ISession> imp
mLockoutCache.setLockoutModeForUser(getTargetUserId(), LockoutTracker.LOCKOUT_TIMED);
// Lockout metrics are logged as an error code.
final int error = BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT;
- logOnError(getContext(), error, 0 /* vendorCode */, getTargetUserId());
+ getLogger().logOnError(getContext(), error, 0 /* vendorCode */,
+ isCryptoOperation(), getTargetUserId());
try {
getListener().onError(getSensorId(), getCookie(), error, 0 /* vendorCode */);
@@ -251,7 +256,8 @@ class FingerprintAuthenticationClient extends AuthenticationClient<ISession> imp
mLockoutCache.setLockoutModeForUser(getTargetUserId(), LockoutTracker.LOCKOUT_PERMANENT);
// Lockout metrics are logged as an error code.
final int error = BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT_PERMANENT;
- logOnError(getContext(), error, 0 /* vendorCode */, getTargetUserId());
+ getLogger().logOnError(getContext(), error, 0 /* vendorCode */,
+ isCryptoOperation(), getTargetUserId());
try {
getListener().onError(getSensorId(), getCookie(), error, 0 /* vendorCode */);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
index ac3ce896049b..1f0482db228b 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
@@ -30,6 +30,7 @@ import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
import com.android.server.biometrics.sensors.AcquisitionClient;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.DetectionConsumer;
import com.android.server.biometrics.sensors.SensorOverlays;
@@ -61,7 +62,7 @@ class FingerprintDetectClient extends AcquisitionClient<ISession> implements Det
}
@Override
- public void start(@NonNull Callback callback) {
+ public void start(@NonNull ClientMonitorCallback callback) {
super.start(callback);
startHalOperation();
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
index 67507ccbbbfe..169c3ebec1a7 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
@@ -37,7 +37,9 @@ import android.util.Slog;
import com.android.server.biometrics.HardwareAuthTokenUtils;
import com.android.server.biometrics.sensors.BiometricNotificationUtils;
import com.android.server.biometrics.sensors.BiometricUtils;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+import com.android.server.biometrics.sensors.ClientMonitorCompositeCallback;
import com.android.server.biometrics.sensors.EnrollClient;
import com.android.server.biometrics.sensors.SensorOverlays;
import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
@@ -76,14 +78,15 @@ class FingerprintEnrollClient extends EnrollClient<ISession> implements Udfps {
mEnrollReason = enrollReason;
if (enrollReason == FingerprintManager.ENROLL_FIND_SENSOR) {
- setShouldLog(false);
+ getLogger().disableMetrics();
}
}
@NonNull
@Override
- protected Callback wrapCallbackForStart(@NonNull Callback callback) {
- return new CompositeCallback(createALSCallback(true /* startWithClient */), callback);
+ protected ClientMonitorCallback wrapCallbackForStart(@NonNull ClientMonitorCallback callback) {
+ return new ClientMonitorCompositeCallback(
+ getLogger().createALSCallback(true /* startWithClient */), callback);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java
index ed2345e3362e..52bd234fcc5d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java
@@ -24,6 +24,7 @@ import android.os.RemoteException;
import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.HalClientMonitor;
import java.util.Map;
@@ -48,7 +49,7 @@ class FingerprintGetAuthenticatorIdClient extends HalClientMonitor<ISession> {
// Nothing to do here
}
- public void start(@NonNull Callback callback) {
+ public void start(@NonNull ClientMonitorCallback callback) {
super.start(callback);
startHalOperation();
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
index eb16c763dea6..efc93045f957 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
@@ -55,7 +55,9 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.sensors.AuthenticationClient;
import com.android.server.biometrics.sensors.BaseClientMonitor;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+import com.android.server.biometrics.sensors.ClientMonitorCompositeCallback;
import com.android.server.biometrics.sensors.InvalidationRequesterClient;
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
import com.android.server.biometrics.sensors.PerformanceTracker;
@@ -248,7 +250,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
}
private void scheduleForSensor(int sensorId, @NonNull BaseClientMonitor client,
- BaseClientMonitor.Callback callback) {
+ ClientMonitorCallback callback) {
if (!mSensors.contains(sensorId)) {
throw new IllegalStateException("Unable to schedule client: " + client
+ " for sensor: " + sensorId);
@@ -361,7 +363,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
opPackageName, FingerprintUtils.getInstance(sensorId), sensorId,
mSensors.get(sensorId).getSensorProperties(),
mUdfpsOverlayController, mSidefpsController, maxTemplatesPerUser, enrollReason);
- scheduleForSensor(sensorId, client, new BaseClientMonitor.Callback() {
+ scheduleForSensor(sensorId, client, new ClientMonitorCallback() {
@Override
public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
@@ -484,7 +486,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
@Override
public void scheduleInternalCleanup(int sensorId, int userId,
- @Nullable BaseClientMonitor.Callback callback) {
+ @Nullable ClientMonitorCallback callback) {
mHandler.post(() -> {
final List<Fingerprint> enrolledList = getEnrolledFingerprints(sensorId, userId);
final FingerprintInternalCleanupClient client =
@@ -493,7 +495,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
mContext.getOpPackageName(), sensorId, enrolledList,
FingerprintUtils.getInstance(sensorId),
mSensors.get(sensorId).getAuthenticatorIds());
- scheduleForSensor(sensorId, client, new BaseClientMonitor.CompositeCallback(callback,
+ scheduleForSensor(sensorId, client, new ClientMonitorCompositeCallback(callback,
mFingerprintStateCallback));
});
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java
index 878ef46d2b2e..ee8d170af407 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java
@@ -27,6 +27,7 @@ import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
import com.android.server.biometrics.HardwareAuthTokenUtils;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ErrorConsumer;
import com.android.server.biometrics.sensors.HalClientMonitor;
import com.android.server.biometrics.sensors.LockoutCache;
@@ -64,7 +65,7 @@ class FingerprintResetLockoutClient extends HalClientMonitor<ISession> implement
}
@Override
- public void start(@NonNull Callback callback) {
+ public void start(@NonNull ClientMonitorCallback callback) {
super.start(callback);
startHalOperation();
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintStartUserClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintStartUserClient.java
index ee81620fdf77..9f11df6b939b 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintStartUserClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintStartUserClient.java
@@ -27,6 +27,7 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.StartUserClient;
public class FingerprintStartUserClient extends StartUserClient<IFingerprint, ISession> {
@@ -44,7 +45,7 @@ public class FingerprintStartUserClient extends StartUserClient<IFingerprint, IS
}
@Override
- public void start(@NonNull Callback callback) {
+ public void start(@NonNull ClientMonitorCallback callback) {
super.start(callback);
startHalOperation();
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintStopUserClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintStopUserClient.java
index 7055d653dd16..9d381459566a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintStopUserClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintStopUserClient.java
@@ -24,6 +24,7 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.StopUserClient;
public class FingerprintStopUserClient extends StopUserClient<ISession> {
@@ -36,7 +37,7 @@ public class FingerprintStopUserClient extends StopUserClient<ISession> {
}
@Override
- public void start(@NonNull Callback callback) {
+ public void start(@NonNull ClientMonitorCallback callback) {
super.start(callback);
startHalOperation();
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java
index 79c6b1b30d5b..033855f822a4 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java
@@ -31,6 +31,7 @@ import android.util.Slog;
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.sensors.BaseClientMonitor;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.fingerprint.FingerprintStateCallback;
import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
@@ -201,7 +202,7 @@ public class BiometricTestSessionImpl extends ITestSession.Stub {
public void cleanupInternalState(int userId) {
Utils.checkPermission(mContext, TEST_BIOMETRIC);
- mFingerprint21.scheduleInternalCleanup(mSensorId, userId, new BaseClientMonitor.Callback() {
+ mFingerprint21.scheduleInternalCleanup(mSensorId, userId, new ClientMonitorCallback() {
@Override
public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
try {
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
index 6feb5fa418bb..f160dfff5249 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
@@ -62,7 +62,9 @@ import com.android.server.biometrics.sensors.AuthenticationClient;
import com.android.server.biometrics.sensors.AuthenticationConsumer;
import com.android.server.biometrics.sensors.BaseClientMonitor;
import com.android.server.biometrics.sensors.BiometricScheduler;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+import com.android.server.biometrics.sensors.ClientMonitorCompositeCallback;
import com.android.server.biometrics.sensors.EnumerateConsumer;
import com.android.server.biometrics.sensors.ErrorConsumer;
import com.android.server.biometrics.sensors.HalClientMonitor;
@@ -492,7 +494,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
new FingerprintUpdateActiveUserClient(mContext, mLazyDaemon, targetUserId,
mContext.getOpPackageName(), mSensorProperties.sensorId,
this::getCurrentUser, hasEnrolled, mAuthenticatorIds, force);
- mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() {
+ mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
@Override
public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
boolean success) {
@@ -577,7 +579,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
FingerprintUtils.getLegacyInstance(mSensorId), ENROLL_TIMEOUT_SEC,
mSensorProperties.sensorId, mUdfpsOverlayController, mSidefpsController,
enrollReason);
- mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() {
+ mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
@Override
public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
mFingerprintStateCallback.onClientStarted(clientMonitor);
@@ -699,7 +701,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
}
private void scheduleInternalCleanup(int userId,
- @Nullable BaseClientMonitor.Callback callback) {
+ @Nullable ClientMonitorCallback callback) {
mHandler.post(() -> {
scheduleUpdateActiveUserWithoutHandler(userId);
@@ -715,8 +717,8 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
@Override
public void scheduleInternalCleanup(int sensorId, int userId,
- @Nullable BaseClientMonitor.Callback callback) {
- scheduleInternalCleanup(userId, new BaseClientMonitor.CompositeCallback(callback,
+ @Nullable ClientMonitorCallback callback) {
+ scheduleInternalCleanup(userId, new ClientMonitorCompositeCallback(callback,
mFingerprintStateCallback));
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
index 273f8a545db5..1694bd92c73c 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
@@ -364,7 +364,7 @@ public class Fingerprint21UdfpsMock extends Fingerprint21 implements TrustManage
final ClientMonitorCallbackConverter listener = client.getListener();
final String opPackageName = client.getOwnerString();
final boolean restricted = authClient.isRestricted();
- final int statsClient = client.getStatsClient();
+ final int statsClient = client.getLogger().getStatsClient();
final boolean isKeyguard = authClient.isKeyguard();
// Don't actually send cancel() to the HAL, since successful auth already finishes
@@ -414,7 +414,8 @@ public class Fingerprint21UdfpsMock extends Fingerprint21 implements TrustManage
}
@Override
- public void onTrustChanged(boolean enabled, int userId, int flags) {
+ public void onTrustChanged(boolean enabled, int userId, int flags,
+ List<String> trustGrantedMessages) {
mUserHasTrust.put(userId, enabled);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
index 3058e2508f5f..87d47c1cffc5 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
@@ -32,9 +32,13 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
+import com.android.server.biometrics.log.CallbackWithProbe;
+import com.android.server.biometrics.log.Probe;
import com.android.server.biometrics.sensors.AuthenticationClient;
import com.android.server.biometrics.sensors.BiometricNotificationUtils;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+import com.android.server.biometrics.sensors.ClientMonitorCompositeCallback;
import com.android.server.biometrics.sensors.LockoutTracker;
import com.android.server.biometrics.sensors.SensorOverlays;
import com.android.server.biometrics.sensors.fingerprint.Udfps;
@@ -80,11 +84,11 @@ class FingerprintAuthenticationClient extends AuthenticationClient<IBiometricsFi
mLockoutFrameworkImpl = lockoutTracker;
mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController);
mSensorProps = sensorProps;
- mALSProbeCallback = createALSCallback(false /* startWithClient */);
+ mALSProbeCallback = getLogger().createALSCallback(false /* startWithClient */);
}
@Override
- public void start(@NonNull Callback callback) {
+ public void start(@NonNull ClientMonitorCallback callback) {
super.start(callback);
if (mSensorProps.isAnyUdfpsType()) {
@@ -97,8 +101,8 @@ class FingerprintAuthenticationClient extends AuthenticationClient<IBiometricsFi
@NonNull
@Override
- protected Callback wrapCallbackForStart(@NonNull Callback callback) {
- return new CompositeCallback(mALSProbeCallback, callback);
+ protected ClientMonitorCallback wrapCallbackForStart(@NonNull ClientMonitorCallback callback) {
+ return new ClientMonitorCompositeCallback(mALSProbeCallback, callback);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
index b854fb300ece..9137212253e8 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
@@ -32,6 +32,7 @@ import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
import com.android.server.biometrics.sensors.AcquisitionClient;
import com.android.server.biometrics.sensors.AuthenticationConsumer;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.PerformanceTracker;
import com.android.server.biometrics.sensors.SensorOverlays;
@@ -82,7 +83,7 @@ class FingerprintDetectClient extends AcquisitionClient<IBiometricsFingerprint>
}
@Override
- public void start(@NonNull Callback callback) {
+ public void start(@NonNull ClientMonitorCallback callback) {
super.start(callback);
startHalOperation();
}
@@ -127,8 +128,8 @@ class FingerprintDetectClient extends AcquisitionClient<IBiometricsFingerprint>
@Override
public void onAuthenticated(BiometricAuthenticator.Identifier identifier, boolean authenticated,
ArrayList<Byte> hardwareAuthToken) {
- logOnAuthenticated(getContext(), authenticated, false /* requireConfirmation */,
- getTargetUserId(), false /* isBiometricPrompt */);
+ getLogger().logOnAuthenticated(getContext(), authenticated, false /* requireConfirmation */,
+ isCryptoOperation(), getTargetUserId(), false /* isBiometricPrompt */);
// Do not distinguish between success/failures.
vibrateSuccess();
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
index cc50bdfb59ae..82b046d0ffd2 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
@@ -33,7 +33,9 @@ import android.util.Slog;
import com.android.server.biometrics.sensors.BiometricNotificationUtils;
import com.android.server.biometrics.sensors.BiometricUtils;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+import com.android.server.biometrics.sensors.ClientMonitorCompositeCallback;
import com.android.server.biometrics.sensors.EnrollClient;
import com.android.server.biometrics.sensors.SensorOverlays;
import com.android.server.biometrics.sensors.fingerprint.Udfps;
@@ -69,14 +71,15 @@ public class FingerprintEnrollClient extends EnrollClient<IBiometricsFingerprint
mEnrollReason = enrollReason;
if (enrollReason == FingerprintManager.ENROLL_FIND_SENSOR) {
- setShouldLog(false);
+ getLogger().disableMetrics();
}
}
@NonNull
@Override
- protected Callback wrapCallbackForStart(@NonNull Callback callback) {
- return new CompositeCallback(createALSCallback(true /* startWithClient */), callback);
+ protected ClientMonitorCallback wrapCallbackForStart(@NonNull ClientMonitorCallback callback) {
+ return new ClientMonitorCompositeCallback(
+ getLogger().createALSCallback(true /* startWithClient */), callback);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintResetLockoutClient.java
index a39f4f8c4d7e..ed28e3ff481e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintResetLockoutClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintResetLockoutClient.java
@@ -22,6 +22,7 @@ import android.hardware.biometrics.BiometricsProtoEnums;
import com.android.server.biometrics.BiometricsProto;
import com.android.server.biometrics.sensors.BaseClientMonitor;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
/**
* Clears lockout, which is handled in the framework (and not the HAL) for the
@@ -40,7 +41,7 @@ public class FingerprintResetLockoutClient extends BaseClientMonitor {
}
@Override
- public void start(@NonNull Callback callback) {
+ public void start(@NonNull ClientMonitorCallback callback) {
super.start(callback);
mLockoutTracker.resetFailedAttemptsForUser(true /* clearAttemptCounter */,
getTargetUserId());
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java
index a2c18923c00e..d317984c140d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java
@@ -27,6 +27,7 @@ import android.os.SELinux;
import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.HalClientMonitor;
import java.io.File;
@@ -62,7 +63,7 @@ public class FingerprintUpdateActiveUserClient extends HalClientMonitor<IBiometr
}
@Override
- public void start(@NonNull Callback callback) {
+ public void start(@NonNull ClientMonitorCallback callback) {
super.start(callback);
if (mCurrentUserId.get() == getTargetUserId() && !mForceUpdateAuthenticatorId) {
diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java
index 3120dc58eebd..1e00ea9161a8 100644
--- a/services/core/java/com/android/server/camera/CameraServiceProxy.java
+++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java
@@ -36,6 +36,7 @@ import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.content.res.Configuration;
+import android.graphics.Rect;
import android.hardware.CameraSessionStats;
import android.hardware.CameraStreamStats;
import android.hardware.ICameraService;
@@ -46,6 +47,8 @@ import android.hardware.camera2.CaptureRequest;
import android.hardware.devicestate.DeviceStateManager;
import android.hardware.devicestate.DeviceStateManager.FoldStateListener;
import android.hardware.display.DisplayManager;
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbManager;
import android.media.AudioManager;
import android.nfc.INfcAdapter;
import android.os.Binder;
@@ -303,6 +306,9 @@ public class CameraServiceProxy extends SystemService
@Override
public void onFixedRotationFinished(int displayId) { }
+
+ @Override
+ public void onKeepClearAreasChanged(int displayId, List<Rect> keepClearArea) { }
}
@@ -335,6 +341,16 @@ public class CameraServiceProxy extends SystemService
switchUserLocked(mLastUser);
}
break;
+ case UsbManager.ACTION_USB_DEVICE_ATTACHED:
+ case UsbManager.ACTION_USB_DEVICE_DETACHED:
+ synchronized (mLock) {
+ UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
+ if (device != null) {
+ notifyUsbDeviceHotplugLocked(device,
+ action.equals(UsbManager.ACTION_USB_DEVICE_ATTACHED));
+ }
+ }
+ break;
default:
break; // do nothing
}
@@ -645,6 +661,8 @@ public class CameraServiceProxy extends SystemService
filter.addAction(Intent.ACTION_USER_INFO_CHANGED);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
+ filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
+ filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
mContext.registerReceiver(mIntentReceiver, filter);
publishBinderService(CAMERA_SERVICE_PROXY_BINDER_NAME, mCameraServiceProxy);
@@ -788,6 +806,7 @@ public class CameraServiceProxy extends SystemService
streamProtos[i].histogramType = streamStats.getHistogramType();
streamProtos[i].histogramBins = streamStats.getHistogramBins();
streamProtos[i].histogramCounts = streamStats.getHistogramCounts();
+ streamProtos[i].dynamicRangeProfile = streamStats.getDynamicRangeProfile();
if (CameraServiceProxy.DEBUG) {
String histogramTypeName =
@@ -807,7 +826,8 @@ public class CameraServiceProxy extends SystemService
+ ", histogramBins "
+ Arrays.toString(streamProtos[i].histogramBins)
+ ", histogramCounts "
- + Arrays.toString(streamProtos[i].histogramCounts));
+ + Arrays.toString(streamProtos[i].histogramCounts)
+ + ", dynamicRangeProfile " + streamProtos[i].dynamicRangeProfile);
}
}
}
@@ -961,6 +981,32 @@ public class CameraServiceProxy extends SystemService
return true;
}
+ private boolean notifyUsbDeviceHotplugLocked(@NonNull UsbDevice device, boolean attached) {
+ // Only handle external USB camera devices
+ if (device.getHasVideoCapture()) {
+ // Forward the usb hotplug event to the native camera service running in the
+ // cameraserver
+ // process.
+ ICameraService cameraService = getCameraServiceRawLocked();
+ if (cameraService == null) {
+ Slog.w(TAG, "Could not notify cameraserver, camera service not available.");
+ return false;
+ }
+
+ try {
+ int eventType = attached ? ICameraService.EVENT_USB_DEVICE_ATTACHED
+ : ICameraService.EVENT_USB_DEVICE_DETACHED;
+ mCameraServiceRaw.notifySystemEvent(eventType, new int[]{device.getDeviceId()});
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Could not notify cameraserver, remote exception: " + e);
+ // Not much we can do if camera service is dead.
+ return false;
+ }
+ return true;
+ }
+ return false;
+ }
+
private void updateActivityCount(CameraSessionStats cameraState) {
String cameraId = cameraState.getCameraId();
int newCameraState = cameraState.getNewCameraState();
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index 582dd7c89e10..5b76695ab0da 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -81,6 +81,7 @@ import com.android.internal.util.FrameworkStatsLog;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.UiThread;
+import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
import com.android.server.contentcapture.ContentCaptureManagerInternal;
import com.android.server.uri.UriGrantsManagerInternal;
import com.android.server.wm.WindowManagerInternal;
@@ -127,6 +128,7 @@ public class ClipboardService extends SystemService {
private final IUriGrantsManager mUgm;
private final UriGrantsManagerInternal mUgmInternal;
private final WindowManagerInternal mWm;
+ private final VirtualDeviceManagerInternal mVdm;
private final IUserManager mUm;
private final PackageManager mPm;
private final AppOpsManager mAppOps;
@@ -158,6 +160,8 @@ public class ClipboardService extends SystemService {
mUgm = UriGrantsManager.getService();
mUgmInternal = LocalServices.getService(UriGrantsManagerInternal.class);
mWm = LocalServices.getService(WindowManagerInternal.class);
+ // Can be null; not all products have CDM + VirtualDeviceManager
+ mVdm = LocalServices.getService(VirtualDeviceManagerInternal.class);
mPm = getContext().getPackageManager();
mUm = (IUserManager) ServiceManager.getService(Context.USER_SERVICE);
mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
@@ -973,6 +977,13 @@ public class ClipboardService extends SystemService {
// First, verify package ownership to ensure use below is safe.
mAppOps.checkPackage(uid, callingPackage);
+ // Nothing in a virtual session is permitted to touch clipboard contents
+ if (mVdm != null && mVdm.isAppRunningOnAnyVirtualDevice(uid)) {
+ Slog.w(TAG, "Clipboard access denied to " + uid + "/" + callingPackage
+ + " within a virtual device session");
+ return false;
+ }
+
// Shell can access the clipboard for testing purposes.
if (mPm.checkPermission(android.Manifest.permission.READ_CLIPBOARD_IN_BACKGROUND,
callingPackage) == PackageManager.PERMISSION_GRANTED) {
diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
index fd4cd8e6ec88..35e3db7832f1 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
@@ -358,6 +358,12 @@ final class DisplayDeviceInfo {
public float brightnessMaximum;
public float brightnessDefault;
+ /**
+ * Install orientation of display panel relative to its natural orientation.
+ */
+ @Surface.Rotation
+ public int installOrientation = Surface.ROTATION_0;
+
public void setAssumedDensityForExternalDisplay(int width, int height) {
densityDpi = Math.min(width, height) * DisplayMetrics.DENSITY_XHIGH / 1080;
// Technically, these values should be smaller than the apparent density
@@ -417,7 +423,8 @@ final class DisplayDeviceInfo {
|| !BrightnessSynchronizer.floatEquals(brightnessMaximum, other.brightnessMaximum)
|| !BrightnessSynchronizer.floatEquals(brightnessDefault,
other.brightnessDefault)
- || !Objects.equals(roundedCorners, other.roundedCorners)) {
+ || !Objects.equals(roundedCorners, other.roundedCorners)
+ || installOrientation != other.installOrientation) {
diff |= DIFF_OTHER;
}
return diff;
@@ -461,6 +468,7 @@ final class DisplayDeviceInfo {
brightnessMaximum = other.brightnessMaximum;
brightnessDefault = other.brightnessDefault;
roundedCorners = other.roundedCorners;
+ installOrientation = other.installOrientation;
}
// For debugging purposes
@@ -508,6 +516,7 @@ final class DisplayDeviceInfo {
sb.append(", roundedCorners ").append(roundedCorners);
}
sb.append(flagsToString(flags));
+ sb.append(", installOrientation ").append(installOrientation);
sb.append("}");
return sb.toString();
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 34f915e41cd7..c6d382923169 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -50,8 +50,6 @@ import android.os.UserHandle;
import android.provider.Settings;
import android.util.Log;
import android.util.MathUtils;
-import android.util.MutableFloat;
-import android.util.MutableInt;
import android.util.Slog;
import android.util.TimeUtils;
import android.view.Display;
@@ -1382,7 +1380,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
// Animate the screen brightness when the screen is on or dozing.
// Skip the animation when the screen is off or suspended or transition to/from VR.
- boolean brightnessAdjusted = false;
if (!mPendingScreenOff) {
if (mSkipScreenOnBrightnessRamp) {
if (state == Display.STATE_ON) {
@@ -1475,19 +1472,15 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
// slider event so notify as if the system changed the brightness.
userInitiatedChange = false;
}
- notifyBrightnessTrackerChanged(brightnessState, userInitiatedChange,
+ notifyBrightnessChanged(brightnessState, userInitiatedChange,
hadUserBrightnessPoint);
}
// We save the brightness info *after* the brightness setting has been changed and
// adjustments made so that the brightness info reflects the latest value.
- brightnessAdjusted = saveBrightnessInfo(getScreenBrightnessSetting(), animateValue);
+ saveBrightnessInfo(getScreenBrightnessSetting(), animateValue);
} else {
- brightnessAdjusted = saveBrightnessInfo(getScreenBrightnessSetting());
- }
-
- if (brightnessAdjusted) {
- postBrightnessChangeRunnable();
+ saveBrightnessInfo(getScreenBrightnessSetting());
}
// Log any changes to what is currently driving the brightness setting.
@@ -1603,50 +1596,31 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
public BrightnessInfo getBrightnessInfo() {
synchronized (mCachedBrightnessInfo) {
return new BrightnessInfo(
- mCachedBrightnessInfo.brightness.value,
- mCachedBrightnessInfo.adjustedBrightness.value,
- mCachedBrightnessInfo.brightnessMin.value,
- mCachedBrightnessInfo.brightnessMax.value,
- mCachedBrightnessInfo.hbmMode.value,
- mCachedBrightnessInfo.hbmTransitionPoint.value);
+ mCachedBrightnessInfo.brightness,
+ mCachedBrightnessInfo.adjustedBrightness,
+ mCachedBrightnessInfo.brightnessMin,
+ mCachedBrightnessInfo.brightnessMax,
+ mCachedBrightnessInfo.hbmMode,
+ mCachedBrightnessInfo.highBrightnessTransitionPoint);
}
}
- private boolean saveBrightnessInfo(float brightness) {
- return saveBrightnessInfo(brightness, brightness);
+ private void saveBrightnessInfo(float brightness) {
+ saveBrightnessInfo(brightness, brightness);
}
- private boolean saveBrightnessInfo(float brightness, float adjustedBrightness) {
+ private void saveBrightnessInfo(float brightness, float adjustedBrightness) {
synchronized (mCachedBrightnessInfo) {
- boolean changed = false;
-
- changed |=
- mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.brightness,
- brightness);
- changed |=
- mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.adjustedBrightness,
- adjustedBrightness);
- changed |=
- mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.brightnessMin,
- mHbmController.getCurrentBrightnessMin());
- changed |=
- mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.brightnessMax,
- mHbmController.getCurrentBrightnessMax());
- changed |=
- mCachedBrightnessInfo.checkAndSetInt(mCachedBrightnessInfo.hbmMode,
- mHbmController.getHighBrightnessMode());
- changed |=
- mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.hbmTransitionPoint,
- mHbmController.getTransitionPoint());
-
- return changed;
+ mCachedBrightnessInfo.brightness = brightness;
+ mCachedBrightnessInfo.adjustedBrightness = adjustedBrightness;
+ mCachedBrightnessInfo.brightnessMin = mHbmController.getCurrentBrightnessMin();
+ mCachedBrightnessInfo.brightnessMax = mHbmController.getCurrentBrightnessMax();
+ mCachedBrightnessInfo.hbmMode = mHbmController.getHighBrightnessMode();
+ mCachedBrightnessInfo.highBrightnessTransitionPoint =
+ mHbmController.getTransitionPoint();
}
}
- void postBrightnessChangeRunnable() {
- mHandler.post(mOnBrightnessChangeRunnable);
- }
-
private HighBrightnessModeController createHbmControllerLocked() {
final DisplayDevice device = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
final DisplayDeviceConfig ddConfig = device.getDisplayDeviceConfig();
@@ -1661,7 +1635,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
displayUniqueId, PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX, hbmData,
() -> {
sendUpdatePowerStateLocked();
- postBrightnessChangeRunnable();
+ mHandler.post(mOnBrightnessChangeRunnable);
// TODO(b/192258832): Switch the HBMChangeCallback to a listener pattern.
if (mAutomaticBrightnessController != null) {
mAutomaticBrightnessController.update();
@@ -2163,7 +2137,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
private void setCurrentScreenBrightness(float brightnessValue) {
if (brightnessValue != mCurrentScreenBrightnessSetting) {
mCurrentScreenBrightnessSetting = brightnessValue;
- postBrightnessChangeRunnable();
+ mHandler.post(mOnBrightnessChangeRunnable);
}
}
@@ -2215,7 +2189,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
return true;
}
- private void notifyBrightnessTrackerChanged(float brightness, boolean userInitiated,
+ private void notifyBrightnessChanged(float brightness, boolean userInitiated,
boolean hadUserDataPoint) {
final float brightnessInNits = convertToNits(brightness);
if (mPowerRequest.useAutoBrightness && brightnessInNits >= 0.0f
@@ -2325,17 +2299,16 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
pw.println(" mColorFadeFadesConfig=" + mColorFadeFadesConfig);
pw.println(" mColorFadeEnabled=" + mColorFadeEnabled);
synchronized (mCachedBrightnessInfo) {
- pw.println(" mCachedBrightnessInfo.brightness=" +
- mCachedBrightnessInfo.brightness.value);
+ pw.println(" mCachedBrightnessInfo.brightness=" + mCachedBrightnessInfo.brightness);
pw.println(" mCachedBrightnessInfo.adjustedBrightness=" +
- mCachedBrightnessInfo.adjustedBrightness.value);
+ mCachedBrightnessInfo.adjustedBrightness);
pw.println(" mCachedBrightnessInfo.brightnessMin=" +
- mCachedBrightnessInfo.brightnessMin.value);
+ mCachedBrightnessInfo.brightnessMin);
pw.println(" mCachedBrightnessInfo.brightnessMax=" +
- mCachedBrightnessInfo.brightnessMax.value);
- pw.println(" mCachedBrightnessInfo.hbmMode=" + mCachedBrightnessInfo.hbmMode.value);
- pw.println(" mCachedBrightnessInfo.hbmTransitionPoint=" +
- mCachedBrightnessInfo.hbmTransitionPoint.value);
+ mCachedBrightnessInfo.brightnessMax);
+ pw.println(" mCachedBrightnessInfo.hbmMode=" + mCachedBrightnessInfo.hbmMode);
+ pw.println(" mCachedBrightnessInfo.highBrightnessTransitionPoint=" +
+ mCachedBrightnessInfo.highBrightnessTransitionPoint);
}
pw.println(" mDisplayBlanksAfterDozeConfig=" + mDisplayBlanksAfterDozeConfig);
pw.println(" mBrightnessBucketsInDozeConfig=" + mBrightnessBucketsInDozeConfig);
@@ -2493,10 +2466,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
private void reportStats(float brightness) {
float hbmTransitionPoint = PowerManager.BRIGHTNESS_MAX;
synchronized(mCachedBrightnessInfo) {
- if (mCachedBrightnessInfo.hbmTransitionPoint == null) {
- return;
- }
- hbmTransitionPoint = mCachedBrightnessInfo.hbmTransitionPoint.value;
+ hbmTransitionPoint = mCachedBrightnessInfo.highBrightnessTransitionPoint;
}
final boolean aboveTransition = brightness > hbmTransitionPoint;
@@ -2793,31 +2763,11 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
}
static class CachedBrightnessInfo {
- public MutableFloat brightness = new MutableFloat(PowerManager.BRIGHTNESS_INVALID_FLOAT);
- public MutableFloat adjustedBrightness =
- new MutableFloat(PowerManager.BRIGHTNESS_INVALID_FLOAT);
- public MutableFloat brightnessMin =
- new MutableFloat(PowerManager.BRIGHTNESS_INVALID_FLOAT);
- public MutableFloat brightnessMax =
- new MutableFloat(PowerManager.BRIGHTNESS_INVALID_FLOAT);
- public MutableInt hbmMode = new MutableInt(BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF);
- public MutableFloat hbmTransitionPoint =
- new MutableFloat(HighBrightnessModeController.HBM_TRANSITION_POINT_INVALID);
-
- public boolean checkAndSetFloat(MutableFloat mf, float f) {
- if (mf.value != f) {
- mf.value = f;
- return true;
- }
- return false;
- }
-
- public boolean checkAndSetInt(MutableInt mi, int i) {
- if (mi.value != i) {
- mi.value = i;
- return true;
- }
- return false;
- }
+ public float brightness;
+ public float adjustedBrightness;
+ public float brightnessMin;
+ public float brightnessMax;
+ public int hbmMode;
+ public float highBrightnessTransitionPoint;
}
}
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 84de8229f37b..3a9ef0a83f6b 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -641,6 +641,7 @@ final class LocalDisplayAdapter extends DisplayAdapter {
mInfo.roundedCorners = RoundedCorners.fromResources(
res, mInfo.uniqueId, mInfo.width, mInfo.height);
+ mInfo.installOrientation = mStaticDisplayInfo.installOrientation;
if (mStaticDisplayInfo.isInternal) {
mInfo.type = Display.TYPE_INTERNAL;
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index 4d1367a3d083..e3ecf498fbb0 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -429,6 +429,7 @@ final class LogicalDisplay {
mBaseDisplayInfo.brightnessMaximum = deviceInfo.brightnessMaximum;
mBaseDisplayInfo.brightnessDefault = deviceInfo.brightnessDefault;
mBaseDisplayInfo.roundedCorners = deviceInfo.roundedCorners;
+ mBaseDisplayInfo.installOrientation = deviceInfo.installOrientation;
mPrimaryDisplayDeviceInfo = deviceInfo;
mInfo.set(null);
}
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
index 7719dfec928b..93c73be5021e 100644
--- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -397,12 +397,16 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
// We already told the displays to turn off, now we need to wake the device as
// we transition to this new state. We do it here so that the waking happens
// between the transition from one layout to another.
- mPowerManager.wakeUp(SystemClock.uptimeMillis(),
- PowerManager.WAKE_REASON_UNFOLD_DEVICE, "server.display:unfold");
+ mHandler.post(() -> {
+ mPowerManager.wakeUp(SystemClock.uptimeMillis(),
+ PowerManager.WAKE_REASON_UNFOLD_DEVICE, "server.display:unfold");
+ });
} else if (sleepDevice) {
// Send the device to sleep when required.
- mPowerManager.goToSleep(SystemClock.uptimeMillis(),
- PowerManager.GO_TO_SLEEP_REASON_DEVICE_FOLD, 0);
+ mHandler.post(() -> {
+ mPowerManager.goToSleep(SystemClock.uptimeMillis(),
+ PowerManager.GO_TO_SLEEP_REASON_DEVICE_FOLD, 0);
+ });
}
}
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 261aa32f093e..2dd7a10ffe29 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -16,6 +16,8 @@
package com.android.server.input;
+import static android.view.KeyEvent.KEYCODE_UNKNOWN;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Notification;
@@ -38,6 +40,7 @@ import android.content.res.Resources.NotFoundException;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.database.ContentObserver;
+import android.graphics.PointF;
import android.graphics.Rect;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayViewport;
@@ -269,6 +272,10 @@ public class InputManagerService extends IInputManager.Stub
private final Map<String, Integer> mRuntimeAssociations = new ArrayMap<String, Integer>();
@GuardedBy("mAssociationLock")
private final Map<String, String> mUniqueIdAssociations = new ArrayMap<>();
+ private final Object mPointerDisplayIdLock = new Object();
+ // Forces the MouseCursorController to target a specific display id.
+ @GuardedBy("mPointerDisplayIdLock")
+ private int mOverriddenPointerDisplayId = Display.INVALID_DISPLAY;
private static native long nativeInit(InputManagerService service,
Context context, MessageQueue messageQueue);
@@ -284,6 +291,8 @@ public class InputManagerService extends IInputManager.Stub
int deviceId, int sourceMask, int sw);
private static native boolean nativeHasKeys(long ptr,
int deviceId, int sourceMask, int[] keyCodes, boolean[] keyExists);
+ private static native int nativeGetKeyCodeForKeyLocation(long ptr, int deviceId,
+ int locationKeyCode);
private static native InputChannel nativeCreateInputChannel(long ptr, String name);
private static native InputChannel nativeCreateInputMonitor(long ptr, int displayId,
boolean isGestureMonitor, String name, int pid);
@@ -341,6 +350,9 @@ public class InputManagerService extends IInputManager.Stub
private static native boolean nativeCanDispatchToDisplay(long ptr, int deviceId, int displayId);
private static native void nativeNotifyPortAssociationsChanged(long ptr);
private static native void nativeChangeUniqueIdAssociation(long ptr);
+ private static native void nativeNotifyPointerDisplayIdChanged(long ptr);
+ private static native void nativeSetDisplayEligibilityForPointerCapture(long ptr, int displayId,
+ boolean enabled);
private static native void nativeSetMotionClassifierEnabled(long ptr, boolean enabled);
private static native InputSensorInfo[] nativeGetSensorList(long ptr, int deviceId);
private static native boolean nativeFlushSensor(long ptr, int deviceId, int sensorType);
@@ -658,6 +670,22 @@ public class InputManagerService extends IInputManager.Stub
}
/**
+ * Returns the keyCode generated by the specified location on a US keyboard layout.
+ * This takes into consideration the currently active keyboard layout.
+ *
+ * @param deviceId The input device id.
+ * @param locationKeyCode The location of a key on a US keyboard layout.
+ * @return The KeyCode this physical key location produces.
+ */
+ @Override // Binder call
+ public int getKeyCodeForKeyLocation(int deviceId, int locationKeyCode) {
+ if (locationKeyCode <= KEYCODE_UNKNOWN || locationKeyCode > KeyEvent.getMaxKeyCode()) {
+ return KEYCODE_UNKNOWN;
+ }
+ return nativeGetKeyCodeForKeyLocation(mPtr, deviceId, locationKeyCode);
+ }
+
+ /**
* Transfer the current touch gesture to the provided window.
*
* @param destChannelToken The token of the window or input channel that should receive the
@@ -1902,6 +1930,18 @@ public class InputManagerService extends IInputManager.Stub
return result;
}
+ private void setVirtualMousePointerDisplayId(int displayId) {
+ synchronized (mPointerDisplayIdLock) {
+ mOverriddenPointerDisplayId = displayId;
+ }
+ // TODO(b/215597605): trigger MousePositionTracker update
+ nativeNotifyPointerDisplayIdChanged(mPtr);
+ }
+
+ private void setDisplayEligibilityForPointerCapture(int displayId, boolean isEligible) {
+ nativeSetDisplayEligibilityForPointerCapture(mPtr, displayId, isEligible);
+ }
+
private static class VibrationInfo {
private final long[] mPattern;
private final int[] mAmplitudes;
@@ -2575,6 +2615,7 @@ public class InputManagerService extends IInputManager.Stub
synchronized (mInputFilterLock) { }
synchronized (mAssociationsLock) { /* Test if blocked by associations lock. */}
synchronized (mLidSwitchLock) { /* Test if blocked by lid switch lock. */ }
+ synchronized (mPointerDisplayIdLock) { /* Test if blocked by pointer display id lock */ }
nativeMonitor(mPtr);
}
@@ -2965,6 +3006,12 @@ public class InputManagerService extends IInputManager.Stub
// Native callback.
private int getPointerDisplayId() {
+ synchronized (mPointerDisplayIdLock) {
+ // Prefer the override to all other displays.
+ if (mOverriddenPointerDisplayId != Display.INVALID_DISPLAY) {
+ return mOverriddenPointerDisplayId;
+ }
+ }
return mWindowManagerCallbacks.getPointerDisplayId();
}
@@ -3109,6 +3156,9 @@ public class InputManagerService extends IInputManager.Stub
int getPointerDisplayId();
+ /** Gets the x and y coordinates of the cursor's current position. */
+ PointF getCursorPosition();
+
/**
* Notifies window manager that a {@link android.view.MotionEvent#ACTION_DOWN} pointer event
* occurred on a window that did not have focus.
@@ -3427,6 +3477,21 @@ public class InputManagerService extends IInputManager.Stub
}
@Override
+ public void setVirtualMousePointerDisplayId(int pointerDisplayId) {
+ InputManagerService.this.setVirtualMousePointerDisplayId(pointerDisplayId);
+ }
+
+ @Override
+ public PointF getCursorPosition() {
+ return mWindowManagerCallbacks.getCursorPosition();
+ }
+
+ @Override
+ public void setDisplayEligibilityForPointerCapture(int displayId, boolean isEligible) {
+ InputManagerService.this.setDisplayEligibilityForPointerCapture(displayId, isEligible);
+ }
+
+ @Override
public void registerLidSwitchCallback(LidSwitchCallback callbacks) {
registerLidSwitchCallbackInternal(callbacks);
}
diff --git a/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java b/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java
new file mode 100644
index 000000000000..c86ebd26d871
--- /dev/null
+++ b/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.inputmethod;
+
+import android.annotation.AnyThread;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Binder;
+import android.os.DeadObjectException;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.util.Slog;
+import android.view.InputChannel;
+import android.view.MotionEvent;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputBinding;
+import android.view.inputmethod.InputMethodSubtype;
+
+import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
+import com.android.internal.view.IInlineSuggestionsRequestCallback;
+import com.android.internal.view.IInputContext;
+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 java.util.List;
+
+/**
+ * A wrapper class to invoke IPCs defined in {@link IInputMethod}.
+ */
+final class IInputMethodInvoker {
+ private static final String TAG = InputMethodManagerService.TAG;
+ private static final boolean DEBUG = InputMethodManagerService.DEBUG;
+
+ @AnyThread
+ @Nullable
+ static IInputMethodInvoker create(@Nullable IInputMethod inputMethod) {
+ if (inputMethod == null) {
+ return null;
+ }
+ if (!Binder.isProxy(inputMethod)) {
+ // IInputMethodInvoker must be used only within the system_server and InputMethodService
+ // must not be running in the system_server. Therefore, "inputMethod" must be a Proxy.
+ throw new UnsupportedOperationException(inputMethod + " must have been a BinderProxy.");
+ }
+ return new IInputMethodInvoker(inputMethod);
+ }
+
+ /**
+ * 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
+ private static void logRemoteException(@NonNull RemoteException e) {
+ if (DEBUG || !(e instanceof DeadObjectException)) {
+ Slog.w(TAG, "IPC failed at IInputMethodInvoker#" + getCallerMethodName(), e);
+ }
+ }
+
+ @AnyThread
+ static int getBinderIdentityHashCode(@Nullable IInputMethodInvoker obj) {
+ if (obj == null) {
+ return 0;
+ }
+
+ return System.identityHashCode(obj.mTarget);
+ }
+
+ @NonNull
+ private final IInputMethod mTarget;
+
+ private IInputMethodInvoker(@NonNull IInputMethod target) {
+ mTarget = target;
+ }
+
+ @AnyThread
+ @NonNull
+ IBinder asBinder() {
+ return mTarget.asBinder();
+ }
+
+ @AnyThread
+ void initializeInternal(IBinder token, IInputMethodPrivilegedOperations privOps,
+ int configChanges, boolean stylusHwSupported) {
+ try {
+ mTarget.initializeInternal(token, privOps, configChanges, stylusHwSupported);
+ } catch (RemoteException e) {
+ logRemoteException(e);
+ }
+ }
+
+ @AnyThread
+ void onCreateInlineSuggestionsRequest(InlineSuggestionsRequestInfo requestInfo,
+ IInlineSuggestionsRequestCallback cb) {
+ try {
+ mTarget.onCreateInlineSuggestionsRequest(requestInfo, cb);
+ } catch (RemoteException e) {
+ logRemoteException(e);
+ }
+ }
+
+ @AnyThread
+ void bindInput(InputBinding binding) {
+ try {
+ mTarget.bindInput(binding);
+ } catch (RemoteException e) {
+ logRemoteException(e);
+ }
+ }
+
+ @AnyThread
+ void unbindInput() {
+ try {
+ mTarget.unbindInput();
+ } catch (RemoteException e) {
+ logRemoteException(e);
+ }
+ }
+
+ @AnyThread
+ void startInput(IBinder startInputToken, IInputContext inputContext, EditorInfo attribute,
+ boolean restarting) {
+ try {
+ mTarget.startInput(startInputToken, inputContext, attribute, restarting);
+ } catch (RemoteException e) {
+ logRemoteException(e);
+ }
+ }
+
+ @AnyThread
+ void createSession(InputChannel channel, IInputSessionCallback callback) {
+ try {
+ mTarget.createSession(channel, callback);
+ } catch (RemoteException e) {
+ logRemoteException(e);
+ }
+ }
+
+ @AnyThread
+ void setSessionEnabled(IInputMethodSession session, boolean enabled) {
+ try {
+ mTarget.setSessionEnabled(session, enabled);
+ } catch (RemoteException e) {
+ logRemoteException(e);
+ }
+ }
+
+ // TODO(b/192412909): Convert this back to void method
+ @AnyThread
+ boolean showSoftInput(IBinder showInputToken, int flags, ResultReceiver resultReceiver) {
+ try {
+ mTarget.showSoftInput(showInputToken, flags, resultReceiver);
+ } catch (RemoteException e) {
+ logRemoteException(e);
+ return false;
+ }
+ return true;
+ }
+
+ // TODO(b/192412909): Convert this back to void method
+ @AnyThread
+ boolean hideSoftInput(IBinder hideInputToken, int flags, ResultReceiver resultReceiver) {
+ try {
+ mTarget.hideSoftInput(hideInputToken, flags, resultReceiver);
+ } catch (RemoteException e) {
+ logRemoteException(e);
+ return false;
+ }
+ return true;
+ }
+
+ @AnyThread
+ void changeInputMethodSubtype(InputMethodSubtype subtype) {
+ try {
+ mTarget.changeInputMethodSubtype(subtype);
+ } catch (RemoteException e) {
+ logRemoteException(e);
+ }
+ }
+
+ @AnyThread
+ void canStartStylusHandwriting(int requestId) {
+ try {
+ mTarget.canStartStylusHandwriting(requestId);
+ } catch (RemoteException e) {
+ logRemoteException(e);
+ }
+ }
+
+ @AnyThread
+ void startStylusHandwriting(InputChannel channel, List<MotionEvent> events) {
+ try {
+ mTarget.startStylusHandwriting(channel, events);
+ } catch (RemoteException e) {
+ logRemoteException(e);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
index db13deba1972..2230dcde0869 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
@@ -75,7 +75,7 @@ final class InputMethodBindingController {
@GuardedBy("ImfLock.class") @Nullable private String mCurId;
@GuardedBy("ImfLock.class") @Nullable private String mSelectedMethodId;
@GuardedBy("ImfLock.class") @Nullable private Intent mCurIntent;
- @GuardedBy("ImfLock.class") @Nullable private IInputMethod mCurMethod;
+ @GuardedBy("ImfLock.class") @Nullable private IInputMethodInvoker mCurMethod;
@GuardedBy("ImfLock.class") private int mCurMethodUid = Process.INVALID_UID;
@GuardedBy("ImfLock.class") private IBinder mCurToken;
@GuardedBy("ImfLock.class") private int mCurSeq;
@@ -241,7 +241,7 @@ final class InputMethodBindingController {
*/
@GuardedBy("ImfLock.class")
@Nullable
- IInputMethod getCurMethod() {
+ IInputMethodInvoker getCurMethod() {
return mCurMethod;
}
@@ -298,7 +298,7 @@ final class InputMethodBindingController {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.onServiceConnected");
synchronized (ImfLock.class) {
if (mCurIntent != null && name.equals(mCurIntent.getComponent())) {
- mCurMethod = IInputMethod.Stub.asInterface(service);
+ mCurMethod = IInputMethodInvoker.create(IInputMethod.Stub.asInterface(service));
updateCurrentMethodUid();
if (mCurToken == null) {
Slog.w(TAG, "Service connected without a token!");
@@ -309,8 +309,8 @@ final class InputMethodBindingController {
if (DEBUG) Slog.v(TAG, "Initiating attach with token: " + mCurToken);
final InputMethodInfo info = mMethodMap.get(mSelectedMethodId);
mSupportsStylusHw = info.supportsStylusHandwriting();
- mService.executeOrSendInitializeIme(mCurMethod, mCurToken,
- info.getConfigChanges(), mSupportsStylusHw);
+ mService.initializeImeLocked(mCurMethod, mCurToken, info.getConfigChanges(),
+ mSupportsStylusHw);
mService.scheduleNotifyImeUidToAudioService(mCurMethodUid);
mService.reRequestCurrentClientSessionLocked();
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index ec49b4768b74..0d41a37caf0d 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -97,7 +97,6 @@ import android.os.Bundle;
import android.os.Debug;
import android.os.Handler;
import android.os.IBinder;
-import android.os.IInterface;
import android.os.LocaleList;
import android.os.Message;
import android.os.Parcel;
@@ -168,7 +167,6 @@ import com.android.internal.util.DumpUtils;
import com.android.internal.view.IInlineSuggestionsRequestCallback;
import com.android.internal.view.IInlineSuggestionsResponseCallback;
import com.android.internal.view.IInputContext;
-import com.android.internal.view.IInputMethod;
import com.android.internal.view.IInputMethodClient;
import com.android.internal.view.IInputMethodManager;
import com.android.internal.view.IInputMethodSession;
@@ -220,22 +218,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
private static final int MSG_SHOW_IM_SUBTYPE_PICKER = 1;
- private static final int MSG_SHOW_IM_SUBTYPE_ENABLER = 2;
- private static final int MSG_SHOW_IM_CONFIG = 3;
- private static final int MSG_UNBIND_INPUT = 1000;
- private static final int MSG_BIND_INPUT = 1010;
- private static final int MSG_SHOW_SOFT_INPUT = 1020;
- private static final int MSG_HIDE_SOFT_INPUT = 1030;
private static final int MSG_HIDE_CURRENT_INPUT_METHOD = 1035;
- private static final int MSG_INITIALIZE_IME = 1040;
- private static final int MSG_CREATE_SESSION = 1050;
private static final int MSG_REMOVE_IME_SURFACE = 1060;
private static final int MSG_REMOVE_IME_SURFACE_FROM_WINDOW = 1061;
private static final int MSG_UPDATE_IME_WINDOW_STATUS = 1070;
- private static final int MSG_START_HANDWRITING = 1100;
-
- private static final int MSG_START_INPUT = 2000;
private static final int MSG_UNBIND_CLIENT = 3000;
private static final int MSG_BIND_CLIENT = 3010;
@@ -248,8 +235,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
private static final int MSG_SYSTEM_UNLOCK_USER = 5000;
private static final int MSG_DISPATCH_ON_INPUT_METHOD_LIST_UPDATED = 5010;
- private static final int MSG_INLINE_SUGGESTIONS_REQUEST = 6000;
-
private static final int MSG_NOTIFY_IME_UID_TO_AUDIO_SERVICE = 7000;
private static final int NOT_A_SUBTYPE_ID = InputMethodUtils.NOT_A_SUBTYPE_ID;
@@ -343,7 +328,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
static class SessionState {
final ClientState client;
- final IInputMethod method;
+ final IInputMethodInvoker method;
IInputMethodSession session;
InputChannel channel;
@@ -352,14 +337,14 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
public String toString() {
return "SessionState{uid " + client.uid + " pid " + client.pid
+ " method " + Integer.toHexString(
- System.identityHashCode(method))
+ IInputMethodInvoker.getBinderIdentityHashCode(method))
+ " session " + Integer.toHexString(
System.identityHashCode(session))
+ " channel " + channel
+ "}";
}
- SessionState(ClientState _client, IInputMethod _method,
+ SessionState(ClientState _client, IInputMethodInvoker _method,
IInputMethodSession _session, InputChannel _channel) {
client = _client;
method = _method;
@@ -627,7 +612,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
*/
@GuardedBy("ImfLock.class")
@Nullable
- private IInputMethod getCurMethodLocked() {
+ private IInputMethodInvoker getCurMethodLocked() {
return mBindingController.getCurMethod();
}
@@ -654,9 +639,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
boolean mBoundToMethod;
/**
- * Currently enabled session. Only touched by service thread, not
- * protected by a lock.
+ * Currently enabled session.
*/
+ @GuardedBy("ImfLock.class")
SessionState mEnabledSession;
/**
@@ -705,11 +690,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
new CopyOnWriteArrayList<>();
/**
- * Internal state snapshot when {@link #MSG_START_INPUT} message is about to be posted to the
- * internal message queue. Any subsequent state change inside {@link InputMethodManagerService}
- * will not affect those tasks that are already posted.
+ * Internal state snapshot when
+ * {@link com.android.internal.view.IInputMethod#startInput(IBinder, IInputContext, EditorInfo,
+ * boolean)} is about to be called.
*
- * <p>Posting {@link #MSG_START_INPUT} message basically means that
+ * <p>Calling that IPC endpoint basically means that
* {@link InputMethodService#doStartInput(InputConnection, EditorInfo, boolean)} will be called
* back in the current IME process shortly, which will also affect what the current IME starts
* receiving from {@link InputMethodService#getCurrentInputConnection()}. In other words, this
@@ -785,7 +770,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
final int mFocusedWindowSoftInputMode;
@SoftInputShowHideReason
final int mReason;
- // The timing of handling MSG_SHOW_SOFT_INPUT or MSG_HIDE_SOFT_INPUT.
+ // The timing of handling showCurrentInputLocked() or hideCurrentInputLocked().
final long mTimestamp;
final long mWallTime;
final boolean mInFullscreenMode;
@@ -1575,7 +1560,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
mHandler.removeCallbacks(mUserSwitchHandlerTask);
}
// Hide soft input before user switch task since switch task may block main handler a while
- // and delayed the MSG_HIDE_SOFT_INPUT.
+ // and delayed the hideCurrentInputLocked().
hideCurrentInputLocked(
mCurFocusedWindow, 0, null, SoftInputShowHideReason.HIDE_SWITCH_USER);
final UserSwitchHandlerTask task = new UserSwitchHandlerTask(this, userId,
@@ -1782,8 +1767,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
com.android.internal.R.bool.show_ongoing_ime_switcher);
if (mShowOngoingImeSwitcherForPhones) {
mWindowManagerInternal.setOnHardKeyboardStatusChangeListener(available -> {
- mHandler.obtainMessage(MSG_HARD_KEYBOARD_SWITCH_CHANGED, available ? 1 : 0)
- .sendToTarget();
+ mHandler.obtainMessage(MSG_HARD_KEYBOARD_SWITCH_CHANGED,
+ available ? 1 : 0, 0 /* unused */).sendToTarget();
});
}
@@ -1965,15 +1950,14 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
InlineSuggestionsRequestInfo requestInfo, IInlineSuggestionsRequestCallback callback) {
final InputMethodInfo imi = mMethodMap.get(getSelectedMethodIdLocked());
try {
- IInputMethod curMethod = getCurMethodLocked();
+ IInputMethodInvoker curMethod = getCurMethodLocked();
if (userId == mSettings.getCurrentUserId() && imi != null
&& imi.isInlineSuggestionsEnabled() && curMethod != null) {
- executeOrSendMessage(curMethod,
- mCaller.obtainMessageOOO(MSG_INLINE_SUGGESTIONS_REQUEST, curMethod,
- requestInfo, new InlineSuggestionsRequestCallbackDecorator(callback,
- imi.getPackageName(), mCurTokenDisplayId,
- getCurTokenLocked(),
- this)));
+ final IInlineSuggestionsRequestCallback callbackImpl =
+ new InlineSuggestionsRequestCallbackDecorator(callback,
+ imi.getPackageName(), mCurTokenDisplayId, getCurTokenLocked(),
+ this);
+ curMethod.onCreateInlineSuggestionsRequest(requestInfo, callbackImpl);
} else {
callback.onInlineSuggestionsUnsupported();
}
@@ -2201,10 +2185,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
mCurFocusedWindow, 0, null, SoftInputShowHideReason.HIDE_REMOVE_CLIENT);
if (mBoundToMethod) {
mBoundToMethod = false;
- IInputMethod curMethod = getCurMethodLocked();
+ IInputMethodInvoker curMethod = getCurMethodLocked();
if (curMethod != null) {
- executeOrSendMessage(curMethod, mCaller.obtainMessageO(
- MSG_UNBIND_INPUT, curMethod));
+ curMethod.unbindInput();
}
}
mCurClient = null;
@@ -2216,8 +2199,14 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
}
- private void executeOrSendMessage(IInterface target, Message msg) {
+ private void executeOrSendMessage(IInputMethodClient target, Message msg) {
if (target.asBinder() instanceof Binder) {
+ // This is supposed to be emulating the one-way semantics when the IME client is
+ // system_server itself, which has not been explicitly prohibited so far while we have
+ // never ever officially supported such a use case...
+ // We probably should create a simple wrapper of IInputMethodClient as the first step
+ // to get rid of executeOrSendMessage() then should prohibit system_server to be the
+ // IME client for long term.
mCaller.sendMessage(msg);
} else {
handleMessage(msg);
@@ -2232,10 +2221,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
+ mCurClient.client.asBinder());
if (mBoundToMethod) {
mBoundToMethod = false;
- IInputMethod curMethod = getCurMethodLocked();
+ IInputMethodInvoker curMethod = getCurMethodLocked();
if (curMethod != null) {
- executeOrSendMessage(curMethod, mCaller.obtainMessageO(
- MSG_UNBIND_INPUT, curMethod));
+ curMethod.unbindInput();
}
}
@@ -2284,16 +2272,15 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
@NonNull
InputBindResult attachNewInputLocked(@StartInputReason int startInputReason, boolean initial) {
if (!mBoundToMethod) {
- IInputMethod curMethod = getCurMethodLocked();
- executeOrSendMessage(curMethod, mCaller.obtainMessageOO(
- MSG_BIND_INPUT, curMethod, mCurClient.binding));
+ getCurMethodLocked().bindInput(mCurClient.binding);
mBoundToMethod = true;
}
+ final boolean restarting = !initial;
final Binder startInputToken = new Binder();
final StartInputInfo info = new StartInputInfo(mSettings.getCurrentUserId(),
getCurTokenLocked(),
- mCurTokenDisplayId, getCurIdLocked(), startInputReason, !initial,
+ mCurTokenDisplayId, getCurIdLocked(), startInputReason, restarting,
UserHandle.getUserId(mCurClient.uid), mCurClient.selfReportedDisplayId,
mCurFocusedWindow, mCurAttribute, mCurFocusedWindowSoftInputMode,
getSequenceNumberLocked());
@@ -2312,9 +2299,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
final SessionState session = mCurClient.curSession;
- executeOrSendMessage(session.method, mCaller.obtainMessageIIOOOO(
- MSG_START_INPUT, 0 /* unused */, initial ? 0 : 1 /* restarting */,
- startInputToken, session, mCurInputContext, mCurAttribute));
+ setEnabledSessionLocked(session);
+ session.method.startInput(startInputToken, mCurInputContext, mCurAttribute, restarting);
+
if (mShowRequested) {
if (DEBUG) Slog.v(TAG, "Attach new input asks to show input");
showCurrentInputLocked(mCurFocusedWindow, getAppShowFlagsLocked(), null,
@@ -2330,11 +2317,20 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
curId, getSequenceNumberLocked(), suppressesSpellChecker);
}
+ /**
+ * Called by {@link #startInputOrWindowGainedFocusInternalLocked} to bind/unbind/attach the
+ * selected InputMethod to the given focused IME client.
+ *
+ * Note that this should be called after validating if the IME client has IME focus.
+ *
+ * @see WindowManagerInternal#hasInputMethodClientFocus(IBinder, int, int, int)
+ */
@GuardedBy("ImfLock.class")
@NonNull
- InputBindResult startInputUncheckedLocked(@NonNull ClientState cs, IInputContext inputContext,
- @NonNull EditorInfo attribute, @StartInputFlags int startInputFlags,
- @StartInputReason int startInputReason, int unverifiedTargetSdkVersion) {
+ private InputBindResult startInputUncheckedLocked(@NonNull ClientState cs,
+ IInputContext inputContext, @NonNull EditorInfo attribute,
+ @StartInputFlags int startInputFlags, @StartInputReason int startInputReason,
+ int unverifiedTargetSdkVersion) {
// If no method is currently selected, do nothing.
String selectedMethodId = getSelectedMethodIdLocked();
if (selectedMethodId == null) {
@@ -2356,10 +2352,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
return InputBindResult.INVALID_PACKAGE_NAME;
}
- if (!mWindowManagerInternal.isUidAllowedOnDisplay(cs.selfReportedDisplayId, cs.uid)) {
- // Wait, the client no longer has access to the display.
- return InputBindResult.INVALID_DISPLAY_ID;
- }
// Compute the final shown display ID with validated cs.selfReportedDisplayId for this
// session & other conditions.
mDisplayIdToShowIme = computeImeDisplayIdForTarget(cs.selfReportedDisplayId,
@@ -2501,11 +2493,15 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
}
- @AnyThread
- void executeOrSendInitializeIme(@NonNull IInputMethod inputMethod, @NonNull IBinder token,
+ @GuardedBy("ImfLock.class")
+ void initializeImeLocked(@NonNull IInputMethodInvoker inputMethod, @NonNull IBinder token,
@android.content.pm.ActivityInfo.Config int configChanges, boolean supportStylusHw) {
- executeOrSendMessage(inputMethod, mCaller.obtainMessageIOOO(MSG_INITIALIZE_IME,
- configChanges, inputMethod, token, supportStylusHw));
+ if (DEBUG) {
+ Slog.v(TAG, "Sending attach of token: " + token + " for display: "
+ + mCurTokenDisplayId);
+ }
+ inputMethod.initializeInternal(token, new InputMethodPrivilegedOperationsImpl(this, token),
+ configChanges, supportStylusHw);
}
@AnyThread
@@ -2515,7 +2511,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
@BinderThread
- void onSessionCreated(IInputMethod method, IInputMethodSession session, InputChannel channel) {
+ void onSessionCreated(IInputMethodInvoker method, IInputMethodSession session,
+ InputChannel channel) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.onSessionCreated");
try {
synchronized (ImfLock.class) {
@@ -2525,7 +2522,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
channel.dispose();
return;
}
- IInputMethod curMethod = getCurMethodLocked();
+ IInputMethodInvoker curMethod = getCurMethodLocked();
if (curMethod != null && method != null
&& curMethod.asBinder() == method.asBinder()) {
if (mCurClient != null) {
@@ -2579,22 +2576,38 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
void requestClientSessionLocked(ClientState cs) {
if (!cs.sessionRequested) {
if (DEBUG) Slog.v(TAG, "Creating new session for client " + cs);
- InputChannel[] channels = InputChannel.openInputChannelPair(cs.toString());
+ final InputChannel serverChannel;
+ final InputChannel clientChannel;
+ {
+ final InputChannel[] channels = InputChannel.openInputChannelPair(cs.toString());
+ serverChannel = channels[0];
+ clientChannel = channels[1];
+ }
+
cs.sessionRequested = true;
- IInputMethod curMethod = getCurMethodLocked();
- executeOrSendMessage(curMethod, mCaller.obtainMessageOOO(
- MSG_CREATE_SESSION, curMethod, channels[1],
- new IInputSessionCallback.Stub() {
- @Override
- public void sessionCreated(IInputMethodSession session) {
- final long ident = Binder.clearCallingIdentity();
- try {
- onSessionCreated(curMethod, session, channels[0]);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
- }));
+
+ final IInputMethodInvoker curMethod = getCurMethodLocked();
+ final IInputSessionCallback.Stub callback = new IInputSessionCallback.Stub() {
+ @Override
+ public void sessionCreated(IInputMethodSession session) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ onSessionCreated(curMethod, session, serverChannel);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ };
+
+ try {
+ curMethod.createSession(clientChannel, callback);
+ } finally {
+ // Dispose the channel because the remote proxy will get its own copy when
+ // unparceled.
+ if (clientChannel != null) {
+ clientChannel.dispose();
+ }
+ }
}
}
@@ -2975,14 +2988,10 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
if (newSubtype != oldSubtype) {
setSelectedInputMethodAndSubtypeLocked(info, subtypeId, true);
- IInputMethod curMethod = getCurMethodLocked();
+ IInputMethodInvoker curMethod = getCurMethodLocked();
if (curMethod != null) {
- try {
- updateSystemUiLocked(mImeWindowVis, mBackDisposition);
- curMethod.changeInputMethodSubtype(newSubtype);
- } catch (RemoteException e) {
- Slog.w(TAG, "Failed to call changeInputMethodSubtype");
- }
+ updateSystemUiLocked(mImeWindowVis, mBackDisposition);
+ curMethod.changeInputMethodSubtype(newSubtype);
}
}
return;
@@ -3059,9 +3068,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
return;
}
if (DEBUG) Slog.v(TAG, "Client requesting Stylus Handwriting to be started");
- if (getCurMethodLocked() != null) {
- executeOrSendMessage(getCurMethodLocked(), mCaller.obtainMessageIO(
- MSG_START_HANDWRITING, ++mHwRequestId, getCurMethodLocked()));
+ final IInputMethodInvoker curMethod = getCurMethodLocked();
+ if (curMethod != null) {
+ curMethod.canStartStylusHandwriting(++mHwRequestId);
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -3112,18 +3121,24 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
mBindingController.setCurrentMethodVisible();
- if (getCurMethodLocked() != null) {
+ final IInputMethodInvoker curMethod = getCurMethodLocked();
+ if (curMethod != null) {
// create a placeholder token for IMS so that IMS cannot inject windows into client app.
Binder showInputToken = new Binder();
mShowRequestWindowMap.put(showInputToken, windowToken);
- IInputMethod curMethod = getCurMethodLocked();
- executeOrSendMessage(curMethod, mCaller.obtainMessageIIOOO(MSG_SHOW_SOFT_INPUT,
- getImeShowFlagsLocked(), reason, curMethod, resultReceiver,
- showInputToken));
+ final int showFlags = getImeShowFlagsLocked();
+ if (DEBUG) {
+ Slog.v(TAG, "Calling " + curMethod + ".showSoftInput(" + showInputToken
+ + ", " + showFlags + ", " + resultReceiver + ") for reason: "
+ + InputMethodDebug.softInputDisplayReasonToString(reason));
+ }
+ // TODO(b/192412909): Check if we can always call onShowHideSoftInputRequested() or not.
+ if (curMethod.showSoftInput(showInputToken, showFlags, resultReceiver)) {
+ onShowHideSoftInputRequested(true /* show */, windowToken, reason);
+ }
mInputShown = true;
return true;
}
-
return false;
}
@@ -3147,14 +3162,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
// be made before input is started in it.
final ClientState cs = mClients.get(client.asBinder());
if (cs == null) {
- throw new IllegalArgumentException(
- "unknown client " + client.asBinder());
+ throw new IllegalArgumentException("unknown client " + client.asBinder());
}
- if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid,
- cs.selfReportedDisplayId)) {
+ if (!isImeClientFocused(windowToken, cs)) {
if (DEBUG) {
- Slog.w(TAG,
- "Ignoring hideSoftInput of uid " + uid + ": " + client);
+ Slog.w(TAG, "Ignoring hideSoftInput of uid " + uid + ": " + client);
}
return false;
}
@@ -3191,7 +3203,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
// since Android Eclair. That's why we need to accept IMM#hideSoftInput() even when only
// IMMS#InputShown indicates that the software keyboard is shown.
// TODO: Clean up, IMMS#mInputShown, IMMS#mImeWindowVis and mShowRequested.
- IInputMethod curMethod = getCurMethodLocked();
+ IInputMethodInvoker curMethod = getCurMethodLocked();
final boolean shouldHideSoftInput = (curMethod != null) && (mInputShown
|| (mImeWindowVis & InputMethodService.IME_ACTIVE) != 0);
boolean res;
@@ -3202,8 +3214,15 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
// delivered to the IME process as an IPC. Hence the inconsistency between
// IMMS#mInputShown and IMMS#mImeWindowVis should be resolved spontaneously in
// the final state.
- executeOrSendMessage(curMethod, mCaller.obtainMessageIOOO(MSG_HIDE_SOFT_INPUT,
- reason, curMethod, resultReceiver, hideInputToken));
+ if (DEBUG) {
+ Slog.v(TAG, "Calling " + curMethod + ".hideSoftInput(0, " + hideInputToken
+ + ", " + resultReceiver + ") for reason: "
+ + InputMethodDebug.softInputDisplayReasonToString(reason));
+ }
+ // TODO(b/192412909): Check if we can always call onShowHideSoftInputRequested() or not.
+ if (curMethod.hideSoftInput(hideInputToken, 0 /* flags */, resultReceiver)) {
+ onShowHideSoftInputRequested(false /* show */, windowToken, reason);
+ }
res = true;
} else {
res = false;
@@ -3216,6 +3235,12 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
return res;
}
+ private boolean isImeClientFocused(IBinder windowToken, ClientState cs) {
+ final int imeClientFocus = mWindowManagerInternal.hasInputMethodClientFocus(
+ windowToken, cs.uid, cs.pid, cs.selfReportedDisplayId);
+ return imeClientFocus == WindowManagerInternal.ImeClientFocusResult.HAS_IME_FOCUS;
+ }
+
@NonNull
@Override
public InputBindResult startInputOrWindowGainedFocus(
@@ -3309,31 +3334,30 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
+ " unverifiedTargetSdkVersion=" + unverifiedTargetSdkVersion);
}
- final int windowDisplayId = mWindowManagerInternal.getDisplayIdForWindow(windowToken);
-
final ClientState cs = mClients.get(client.asBinder());
if (cs == null) {
throw new IllegalArgumentException("unknown client " + client.asBinder());
}
- if (cs.selfReportedDisplayId != windowDisplayId) {
- Slog.e(TAG, "startInputOrWindowGainedFocusInternal: display ID mismatch."
- + " from client:" + cs.selfReportedDisplayId
- + " from window:" + windowDisplayId);
- return InputBindResult.DISPLAY_ID_MISMATCH;
- }
- if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid,
- cs.selfReportedDisplayId)) {
- // Check with the window manager to make sure this client actually
- // has a window with focus. If not, reject. This is thread safe
- // because if the focus changes some time before or after, the
- // next client receiving focus that has any interest in input will
- // be calling through here after that change happens.
- if (DEBUG) {
- Slog.w(TAG, "Focus gain on non-focused client " + cs.client
- + " (uid=" + cs.uid + " pid=" + cs.pid + ")");
- }
- return InputBindResult.NOT_IME_TARGET_WINDOW;
+ final int imeClientFocus = mWindowManagerInternal.hasInputMethodClientFocus(
+ windowToken, cs.uid, cs.pid, cs.selfReportedDisplayId);
+ switch (imeClientFocus) {
+ case WindowManagerInternal.ImeClientFocusResult.DISPLAY_ID_MISMATCH:
+ Slog.e(TAG, "startInputOrWindowGainedFocusInternal: display ID mismatch.");
+ return InputBindResult.DISPLAY_ID_MISMATCH;
+ case WindowManagerInternal.ImeClientFocusResult.NOT_IME_TARGET_WINDOW:
+ // Check with the window manager to make sure this client actually
+ // has a window with focus. If not, reject. This is thread safe
+ // because if the focus changes some time before or after, the
+ // next client receiving focus that has any interest in input will
+ // be calling through here after that change happens.
+ if (DEBUG) {
+ Slog.w(TAG, "Focus gain on non-focused client " + cs.client
+ + " (uid=" + cs.uid + " pid=" + cs.pid + ")");
+ }
+ return InputBindResult.NOT_IME_TARGET_WINDOW;
+ case WindowManagerInternal.ImeClientFocusResult.INVALID_DISPLAY_ID:
+ return InputBindResult.INVALID_DISPLAY_ID;
}
if (mUserSwitchHandlerTask != null) {
@@ -3561,8 +3585,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
if (cs == null) {
throw new IllegalArgumentException("unknown client " + client.asBinder());
}
- if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid,
- cs.selfReportedDisplayId)) {
+ if (!isImeClientFocused(mCurFocusedWindow, cs)) {
Slog.w(TAG, String.format("Ignoring %s of uid %d : %s", methodName, uid, client));
return false;
}
@@ -3679,8 +3702,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
if (!calledFromValidUserLocked()) {
return;
}
- executeOrSendMessage(getCurMethodLocked(), mCaller.obtainMessageO(
- MSG_SHOW_IM_SUBTYPE_ENABLER, inputMethodId));
+ showInputMethodAndSubtypeEnabler(inputMethodId);
}
}
@@ -4104,7 +4126,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
}
- /** Called right after {@link IInputMethod#showSoftInput}. */
+ /** Called right after {@link com.android.internal.view.IInputMethod#showSoftInput}. */
@GuardedBy("ImfLock.class")
private void onShowHideSoftInputRequested(boolean show, IBinder requestToken,
@SoftInputShowHideReason int reason) {
@@ -4155,22 +4177,17 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
- void setEnabledSessionInHandlerThread(SessionState session) {
+ @GuardedBy("ImfLock.class")
+ void setEnabledSessionLocked(SessionState session) {
if (mEnabledSession != session) {
if (mEnabledSession != null && mEnabledSession.session != null) {
- try {
- if (DEBUG) Slog.v(TAG, "Disabling: " + mEnabledSession);
- mEnabledSession.method.setSessionEnabled(mEnabledSession.session, false);
- } catch (RemoteException e) {
- }
+ if (DEBUG) Slog.v(TAG, "Disabling: " + mEnabledSession);
+ mEnabledSession.method.setSessionEnabled(mEnabledSession.session, false);
}
mEnabledSession = session;
if (mEnabledSession != null && mEnabledSession.session != null) {
- try {
- if (DEBUG) Slog.v(TAG, "Enabling: " + mEnabledSession);
- mEnabledSession.method.setSessionEnabled(mEnabledSession.session, true);
- } catch (RemoteException e) {
- }
+ if (DEBUG) Slog.v(TAG, "Enabling: " + mEnabledSession);
+ mEnabledSession.method.setSessionEnabled(mEnabledSession.session, true);
}
}
}
@@ -4203,69 +4220,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
mMenuController.showInputMethodMenu(showAuxSubtypes, displayId);
return true;
- case MSG_SHOW_IM_SUBTYPE_ENABLER:
- showInputMethodAndSubtypeEnabler((String)msg.obj);
- return true;
-
- case MSG_SHOW_IM_CONFIG:
- showConfigureInputMethods();
- return true;
-
// ---------------------------------------------------------
- case MSG_UNBIND_INPUT:
- try {
- ((IInputMethod)msg.obj).unbindInput();
- } catch (RemoteException e) {
- // There is nothing interesting about the method dying.
- }
- return true;
- case MSG_BIND_INPUT:
- args = (SomeArgs)msg.obj;
- try {
- ((IInputMethod)args.arg1).bindInput((InputBinding)args.arg2);
- } catch (RemoteException e) {
- }
- args.recycle();
- return true;
- case MSG_SHOW_SOFT_INPUT:
- args = (SomeArgs) msg.obj;
- try {
- final @SoftInputShowHideReason int reason = msg.arg2;
- if (DEBUG) Slog.v(TAG, "Calling " + args.arg1 + ".showSoftInput("
- + args.arg3 + ", " + msg.arg1 + ", " + args.arg2 + ") for reason: "
- + InputMethodDebug.softInputDisplayReasonToString(reason));
- final IBinder token = (IBinder) args.arg3;
- ((IInputMethod) args.arg1).showSoftInput(
- token, msg.arg1 /* flags */, (ResultReceiver) args.arg2);
- final IBinder requestToken;
- synchronized (ImfLock.class) {
- requestToken = mShowRequestWindowMap.get(token);
- onShowHideSoftInputRequested(true /* show */, requestToken, reason);
- }
- } catch (RemoteException e) {
- }
- args.recycle();
- return true;
- case MSG_HIDE_SOFT_INPUT:
- args = (SomeArgs) msg.obj;
- try {
- final @SoftInputShowHideReason int reason = msg.arg1;
- if (DEBUG) Slog.v(TAG, "Calling " + args.arg1 + ".hideSoftInput(0, "
- + args.arg3 + ", " + args.arg2 + ") for reason: "
- + InputMethodDebug.softInputDisplayReasonToString(reason));
- final IBinder token = (IBinder) args.arg3;
- ((IInputMethod)args.arg1).hideSoftInput(
- token, 0 /* flags */, (ResultReceiver) args.arg2);
- final IBinder requestToken;
- synchronized (ImfLock.class) {
- requestToken = mHideRequestWindowMap.get(token);
- onShowHideSoftInputRequested(false /* show */, requestToken, reason);
- }
- } catch (RemoteException e) {
- }
- args.recycle();
- return true;
case MSG_HIDE_CURRENT_INPUT_METHOD:
synchronized (ImfLock.class) {
final @SoftInputShowHideReason int reason = (int) msg.obj;
@@ -4273,40 +4229,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
return true;
- case MSG_INITIALIZE_IME:
- args = (SomeArgs)msg.obj;
- try {
- if (DEBUG) {
- synchronized (ImfLock.class) {
- Slog.v(TAG, "Sending attach of token: " + args.arg2 + " for display: "
- + mCurTokenDisplayId);
- }
- }
- final IBinder token = (IBinder) args.arg2;
- ((IInputMethod) args.arg1).initializeInternal(token,
- new InputMethodPrivilegedOperationsImpl(this, token),
- msg.arg1, (boolean) args.arg3);
- } catch (RemoteException e) {
- }
- args.recycle();
- return true;
- case MSG_CREATE_SESSION: {
- args = (SomeArgs)msg.obj;
- IInputMethod method = (IInputMethod)args.arg1;
- InputChannel channel = (InputChannel)args.arg2;
- try {
- method.createSession(channel, (IInputSessionCallback)args.arg3);
- } catch (RemoteException e) {
- } finally {
- // Dispose the channel if the input method is not local to this process
- // because the remote proxy will get its own copy when unparceled.
- if (channel != null && Binder.isProxy(method)) {
- channel.dispose();
- }
- }
- args.recycle();
- return true;
- }
case MSG_REMOVE_IME_SURFACE: {
synchronized (ImfLock.class) {
try {
@@ -4338,25 +4260,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
// ---------------------------------------------------------
- case MSG_START_INPUT: {
- final boolean restarting = msg.arg2 != 0;
- args = (SomeArgs) msg.obj;
- final IBinder startInputToken = (IBinder) args.arg1;
- final SessionState session = (SessionState) args.arg2;
- final IInputContext inputContext = (IInputContext) args.arg3;
- final EditorInfo editorInfo = (EditorInfo) args.arg4;
- try {
- setEnabledSessionInHandlerThread(session);
- session.method.startInput(startInputToken, inputContext, editorInfo,
- restarting);
- } catch (RemoteException e) {
- }
- args.recycle();
- return true;
- }
-
- // ---------------------------------------------------------
-
case MSG_UNBIND_CLIENT:
try {
((IInputMethodClient)msg.obj).onUnbindMethod(msg.arg1, msg.arg2);
@@ -4430,23 +4333,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
// ---------------------------------------------------------------
- case MSG_INLINE_SUGGESTIONS_REQUEST: {
- args = (SomeArgs) msg.obj;
- final InlineSuggestionsRequestInfo requestInfo =
- (InlineSuggestionsRequestInfo) args.arg2;
- final IInlineSuggestionsRequestCallback callback =
- (IInlineSuggestionsRequestCallback) args.arg3;
- try {
- ((IInputMethod) args.arg1).onCreateInlineSuggestionsRequest(requestInfo,
- callback);
- } catch (RemoteException e) {
- Slog.w(TAG, "RemoteException calling onCreateInlineSuggestionsRequest(): " + e);
- }
- args.recycle();
- return true;
- }
-
- // ---------------------------------------------------------------
case MSG_NOTIFY_IME_UID_TO_AUDIO_SERVICE: {
if (mAudioManagerInternal == null) {
mAudioManagerInternal = LocalServices.getService(AudioManagerInternal.class);
@@ -4456,13 +4342,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
return true;
}
- case MSG_START_HANDWRITING:
- try {
- (((IInputMethod) msg.obj)).canStartStylusHandwriting(msg.arg1);
- } catch (RemoteException e) {
- Slog.w(TAG, "RemoteException calling canStartStylusHandwriting(): ", e);
- }
- return true;
}
return false;
}
@@ -4475,12 +4354,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
return;
}
- try {
- // TODO: replace null with actual Channel, MotionEvents
- getCurMethodLocked().startStylusHandwriting(null, null);
- } catch (RemoteException e) {
- Slog.w(TAG, "RemoteException calling startStylusHandwriting(): ", e);
- }
+ // TODO: replace null with actual Channel, MotionEvents
+ getCurMethodLocked().startStylusHandwriting(null, null);
}
}
@@ -4741,14 +4616,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
mContext.startActivityAsUser(intent, null, UserHandle.of(userId));
}
- private void showConfigureInputMethods() {
- Intent intent = new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
- | Intent.FLAG_ACTIVITY_CLEAR_TOP);
- mContext.startActivityAsUser(intent, null, UserHandle.CURRENT);
- }
-
// ----------------------------------------------------------------------
/**
@@ -5221,7 +5088,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
@BinderThread
private void dumpAsStringNoCheck(FileDescriptor fd, PrintWriter pw, String[] args,
boolean isCritical) {
- IInputMethod method;
+ IInputMethodInvoker method;
ClientState client;
ClientState focusedWindowClient;
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 f1141845f2bd..c02411ec4c1b 100644
--- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
@@ -16,14 +16,15 @@
package com.android.server.location.gnss;
-import static android.content.pm.PackageManager.FEATURE_WATCH;
-
import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP;
+import static android.content.pm.PackageManager.FEATURE_WATCH;
import static android.location.provider.ProviderProperties.ACCURACY_FINE;
import static android.location.provider.ProviderProperties.POWER_USAGE_HIGH;
import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
import static com.android.server.location.gnss.hal.GnssNative.AGPS_REF_LOCATION_TYPE_GSM_CELLID;
+import static com.android.server.location.gnss.hal.GnssNative.AGPS_REF_LOCATION_TYPE_LTE_CELLID;
+import static com.android.server.location.gnss.hal.GnssNative.AGPS_REF_LOCATION_TYPE_NR_CELLID;
import static com.android.server.location.gnss.hal.GnssNative.AGPS_REF_LOCATION_TYPE_UMTS_CELLID;
import static com.android.server.location.gnss.hal.GnssNative.AGPS_SETID_TYPE_IMSI;
import static com.android.server.location.gnss.hal.GnssNative.AGPS_SETID_TYPE_MSISDN;
@@ -84,9 +85,18 @@ import android.os.WorkSource;
import android.os.WorkSource.WorkChain;
import android.provider.Settings;
import android.telephony.CarrierConfigManager;
+import android.telephony.CellIdentity;
+import android.telephony.CellIdentityGsm;
+import android.telephony.CellIdentityLte;
+import android.telephony.CellIdentityNr;
+import android.telephony.CellIdentityWcdma;
+import android.telephony.CellInfo;
+import android.telephony.CellInfoGsm;
+import android.telephony.CellInfoLte;
+import android.telephony.CellInfoNr;
+import android.telephony.CellInfoWcdma;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
-import android.telephony.gsm.GsmCellLocation;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.Log;
@@ -110,6 +120,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
@@ -1136,7 +1147,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
if (DEBUG) {
Log.d(TAG, "startBatching " + mFixInterval + " " + batchLengthMs);
}
- if (mGnssNative.startBatch(MILLISECONDS.toNanos(mFixInterval), true)) {
+ if (mGnssNative.startBatch(MILLISECONDS.toNanos(mFixInterval), 0, true)) {
mBatchingStarted = true;
if (batchSize < getBatchSize()) {
@@ -1386,29 +1397,127 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
postWithWakeLockHeld(mNtpTimeHelper::retrieveAndInjectNtpTime);
}
+
+ private static int getCellType(CellInfo ci) {
+ if (ci instanceof CellInfoGsm) {
+ return CellInfo.TYPE_GSM;
+ } else if (ci instanceof CellInfoWcdma) {
+ return CellInfo.TYPE_WCDMA;
+ } else if (ci instanceof CellInfoLte) {
+ return CellInfo.TYPE_LTE;
+ } else if (ci instanceof CellInfoNr) {
+ return CellInfo.TYPE_NR;
+ }
+ return CellInfo.TYPE_UNKNOWN;
+ }
+
+ /**
+ * Extract the CID/CI for GSM/WCDMA/LTE/NR
+ *
+ * @return the cell ID or -1 if invalid
+ */
+ private static long getCidFromCellIdentity(CellIdentity id) {
+ if (id == null) return -1;
+ long cid = -1;
+ switch(id.getType()) {
+ case CellInfo.TYPE_GSM: cid = ((CellIdentityGsm) id).getCid(); break;
+ case CellInfo.TYPE_WCDMA: cid = ((CellIdentityWcdma) id).getCid(); break;
+ case CellInfo.TYPE_LTE: cid = ((CellIdentityLte) id).getCi(); break;
+ case CellInfo.TYPE_NR: cid = ((CellIdentityNr) id).getNci(); break;
+ default: break;
+ }
+ // If the CID is unreported
+ if (cid == (id.getType() == CellInfo.TYPE_NR
+ ? CellInfo.UNAVAILABLE_LONG : CellInfo.UNAVAILABLE)) {
+ cid = -1;
+ }
+
+ return cid;
+ }
+
+ private void setRefLocation(int type, CellIdentity ci) {
+ String mcc_str = ci.getMccString();
+ String mnc_str = ci.getMncString();
+ int mcc = mcc_str != null ? Integer.parseInt(mcc_str) : CellInfo.UNAVAILABLE;
+ int mnc = mnc_str != null ? Integer.parseInt(mnc_str) : CellInfo.UNAVAILABLE;
+ int lac = CellInfo.UNAVAILABLE;
+ int tac = CellInfo.UNAVAILABLE;
+ int pcid = CellInfo.UNAVAILABLE;
+ int arfcn = CellInfo.UNAVAILABLE;
+ long cid = CellInfo.UNAVAILABLE_LONG;
+
+ switch (type) {
+ case AGPS_REF_LOCATION_TYPE_GSM_CELLID:
+ CellIdentityGsm cig = (CellIdentityGsm) ci;
+ cid = cig.getCid();
+ lac = cig.getLac();
+ break;
+ case AGPS_REF_LOCATION_TYPE_UMTS_CELLID:
+ CellIdentityWcdma ciw = (CellIdentityWcdma) ci;
+ cid = ciw.getCid();
+ lac = ciw.getLac();
+ break;
+ case AGPS_REF_LOCATION_TYPE_LTE_CELLID:
+ CellIdentityLte cil = (CellIdentityLte) ci;
+ cid = cil.getCi();
+ tac = cil.getTac();
+ pcid = cil.getPci();
+ break;
+ case AGPS_REF_LOCATION_TYPE_NR_CELLID:
+ CellIdentityNr cin = (CellIdentityNr) ci;
+ cid = cin.getNci();
+ tac = cin.getTac();
+ pcid = cin.getPci();
+ arfcn = cin.getNrarfcn();
+ break;
+ default:
+ }
+
+ mGnssNative.setAgpsReferenceLocationCellId(
+ type, mcc, mnc, lac, cid, tac, pcid, arfcn);
+ }
+
private void requestRefLocation() {
TelephonyManager phone = (TelephonyManager)
mContext.getSystemService(Context.TELEPHONY_SERVICE);
+
final int phoneType = phone.getPhoneType();
if (phoneType == TelephonyManager.PHONE_TYPE_GSM) {
- GsmCellLocation gsm_cell = (GsmCellLocation) phone.getCellLocation();
- if ((gsm_cell != null) && (phone.getNetworkOperator() != null)
- && (phone.getNetworkOperator().length() > 3)) {
- int type;
- int mcc = Integer.parseInt(phone.getNetworkOperator().substring(0, 3));
- int mnc = Integer.parseInt(phone.getNetworkOperator().substring(3));
- int networkType = phone.getNetworkType();
- if (networkType == TelephonyManager.NETWORK_TYPE_UMTS
- || networkType == TelephonyManager.NETWORK_TYPE_HSDPA
- || networkType == TelephonyManager.NETWORK_TYPE_HSUPA
- || networkType == TelephonyManager.NETWORK_TYPE_HSPA
- || networkType == TelephonyManager.NETWORK_TYPE_HSPAP) {
- type = AGPS_REF_LOCATION_TYPE_UMTS_CELLID;
+
+ List<CellInfo> cil = phone.getAllCellInfo();
+ if (cil != null) {
+ HashMap<Integer, CellIdentity> cellIdentityMap = new HashMap<>();
+ cil.sort(Comparator.comparingInt(
+ (CellInfo ci) -> ci.getCellSignalStrength().getAsuLevel()).reversed());
+
+ for (CellInfo ci : cil) {
+ int status = ci.getCellConnectionStatus();
+ if (status == CellInfo.CONNECTION_PRIMARY_SERVING
+ || status == CellInfo.CONNECTION_SECONDARY_SERVING) {
+ CellIdentity c = ci.getCellIdentity();
+ int t = getCellType(ci);
+ if (getCidFromCellIdentity(c) != -1
+ && !cellIdentityMap.containsKey(t)) {
+ cellIdentityMap.put(t, c);
+ }
+ }
+ }
+
+ if (cellIdentityMap.containsKey(CellInfo.TYPE_GSM)) {
+ setRefLocation(AGPS_REF_LOCATION_TYPE_GSM_CELLID,
+ cellIdentityMap.get(CellInfo.TYPE_GSM));
+ } else if (cellIdentityMap.containsKey(CellInfo.TYPE_WCDMA)) {
+ setRefLocation(AGPS_REF_LOCATION_TYPE_UMTS_CELLID,
+ cellIdentityMap.get(CellInfo.TYPE_WCDMA));
+ } else if (cellIdentityMap.containsKey(CellInfo.TYPE_LTE)) {
+ setRefLocation(AGPS_REF_LOCATION_TYPE_LTE_CELLID,
+ cellIdentityMap.get(CellInfo.TYPE_LTE));
+ } else if (cellIdentityMap.containsKey(CellInfo.TYPE_NR)) {
+ setRefLocation(AGPS_REF_LOCATION_TYPE_NR_CELLID,
+ cellIdentityMap.get(CellInfo.TYPE_NR));
} else {
- type = AGPS_REF_LOCATION_TYPE_GSM_CELLID;
+ Log.e(TAG, "No available serving cell information.");
}
- mGnssNative.setAgpsReferenceLocationCellId(type, mcc, mnc, gsm_cell.getLac(),
- gsm_cell.getCid());
} else {
Log.e(TAG, "Error getting cell location info.");
}
diff --git a/services/core/java/com/android/server/location/gnss/hal/GnssNative.java b/services/core/java/com/android/server/location/gnss/hal/GnssNative.java
index a513e0898344..e072bf7dc1f7 100644
--- a/services/core/java/com/android/server/location/gnss/hal/GnssNative.java
+++ b/services/core/java/com/android/server/location/gnss/hal/GnssNative.java
@@ -124,9 +124,12 @@ public class GnssNative {
// IMPORTANT - must match OEM definitions, this isn't part of a hal for some reason
public static final int AGPS_REF_LOCATION_TYPE_GSM_CELLID = 1;
public static final int AGPS_REF_LOCATION_TYPE_UMTS_CELLID = 2;
+ public static final int AGPS_REF_LOCATION_TYPE_LTE_CELLID = 4;
+ public static final int AGPS_REF_LOCATION_TYPE_NR_CELLID = 8;
@IntDef(prefix = "AGPS_REF_LOCATION_TYPE_", value = {AGPS_REF_LOCATION_TYPE_GSM_CELLID,
- AGPS_REF_LOCATION_TYPE_UMTS_CELLID})
+ AGPS_REF_LOCATION_TYPE_UMTS_CELLID, AGPS_REF_LOCATION_TYPE_LTE_CELLID,
+ AGPS_REF_LOCATION_TYPE_NR_CELLID})
@Retention(RetentionPolicy.SOURCE)
public @interface AgpsReferenceLocationType {}
@@ -814,9 +817,10 @@ public class GnssNative {
/**
* Start batching.
*/
- public boolean startBatch(long periodNanos, boolean wakeOnFifoFull) {
+ public boolean startBatch(long periodNanos, float minUpdateDistanceMeters,
+ boolean wakeOnFifoFull) {
Preconditions.checkState(mRegistered);
- return mGnssHal.startBatch(periodNanos, wakeOnFifoFull);
+ return mGnssHal.startBatch(periodNanos, minUpdateDistanceMeters, wakeOnFifoFull);
}
/**
@@ -930,9 +934,9 @@ public class GnssNative {
* Sets AGPS reference cell id location.
*/
public void setAgpsReferenceLocationCellId(@AgpsReferenceLocationType int type, int mcc,
- int mnc, int lac, int cid) {
+ int mnc, int lac, long cid, int tac, int pcid, int arfcn) {
Preconditions.checkState(mRegistered);
- mGnssHal.setAgpsReferenceLocationCellId(type, mcc, mnc, lac, cid);
+ mGnssHal.setAgpsReferenceLocationCellId(type, mcc, mnc, lac, cid, tac, pcid, arfcn);
}
/**
@@ -1377,8 +1381,9 @@ public class GnssNative {
native_cleanup_batching();
}
- protected boolean startBatch(long periodNanos, boolean wakeOnFifoFull) {
- return native_start_batch(periodNanos, wakeOnFifoFull);
+ protected boolean startBatch(long periodNanos, float minUpdateDistanceMeters,
+ boolean wakeOnFifoFull) {
+ return native_start_batch(periodNanos, minUpdateDistanceMeters, wakeOnFifoFull);
}
protected void flushBatch() {
@@ -1433,8 +1438,8 @@ public class GnssNative {
}
protected void setAgpsReferenceLocationCellId(@AgpsReferenceLocationType int type, int mcc,
- int mnc, int lac, int cid) {
- native_agps_set_ref_location_cellid(type, mcc, mnc, lac, cid);
+ int mnc, int lac, long cid, int tac, int pcid, int arfcn) {
+ native_agps_set_ref_location_cellid(type, mcc, mnc, lac, cid, tac, pcid, arfcn);
}
protected boolean isPsdsSupported() {
@@ -1536,7 +1541,8 @@ public class GnssNative {
private static native void native_cleanup_batching();
- private static native boolean native_start_batch(long periodNanos, boolean wakeOnFifoFull);
+ private static native boolean native_start_batch(long periodNanos,
+ float minUpdateDistanceMeters, boolean wakeOnFifoFull);
private static native void native_flush_batch();
@@ -1575,7 +1581,7 @@ public class GnssNative {
private static native void native_agps_set_id(int type, String setid);
private static native void native_agps_set_ref_location_cellid(int type, int mcc, int mnc,
- int lac, int cid);
+ int lac, long cid, int tac, int pcid, int arfcn);
// PSDS APIs
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 682a27adc15f..8f051303bf03 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -20,6 +20,9 @@ import static android.Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE;
import static android.Manifest.permission.MANAGE_BIOMETRIC;
import static android.Manifest.permission.READ_CONTACTS;
import static android.Manifest.permission.SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS;
+import static android.app.admin.DevicePolicyResources.Strings.Core.PROFILE_ENCRYPTED_DETAIL;
+import static android.app.admin.DevicePolicyResources.Strings.Core.PROFILE_ENCRYPTED_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.PROFILE_ENCRYPTED_TITLE;
import static android.content.Context.KEYGUARD_SERVICE;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.UserHandle.USER_ALL;
@@ -117,6 +120,7 @@ import android.util.LongSparseArray;
import android.util.Slog;
import android.util.SparseArray;
+import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
@@ -642,12 +646,9 @@ public class LockSettingsService extends ILockSettings.Stub {
private void showEncryptionNotificationForProfile(UserHandle user) {
Resources r = mContext.getResources();
- CharSequence title = r.getText(
- com.android.internal.R.string.profile_encrypted_title);
- CharSequence message = r.getText(
- com.android.internal.R.string.profile_encrypted_message);
- CharSequence detail = r.getText(
- com.android.internal.R.string.profile_encrypted_detail);
+ CharSequence title = getEncryptionNotificationTitle();
+ CharSequence message = getEncryptionNotificationMessage();
+ CharSequence detail = getEncryptionNotificationDetail();
final KeyguardManager km = (KeyguardManager) mContext.getSystemService(KEYGUARD_SERVICE);
final Intent unlockIntent = km.createConfirmDeviceCredentialIntent(null, null,
@@ -663,6 +664,24 @@ public class LockSettingsService extends ILockSettings.Stub {
showEncryptionNotification(user, title, message, detail, intent);
}
+ private String getEncryptionNotificationTitle() {
+ return mInjector.getDevicePolicyManager().getString(
+ PROFILE_ENCRYPTED_TITLE,
+ () -> mContext.getString(R.string.profile_encrypted_title));
+ }
+
+ private String getEncryptionNotificationDetail() {
+ return mInjector.getDevicePolicyManager().getString(
+ PROFILE_ENCRYPTED_DETAIL,
+ () -> mContext.getString(R.string.profile_encrypted_detail));
+ }
+
+ private String getEncryptionNotificationMessage() {
+ return mInjector.getDevicePolicyManager().getString(
+ PROFILE_ENCRYPTED_MESSAGE,
+ () -> mContext.getString(R.string.profile_encrypted_message));
+ }
+
private void showEncryptionNotification(UserHandle user, CharSequence title,
CharSequence message, CharSequence detail, PendingIntent intent) {
if (DEBUG) Slog.v(TAG, "showing encryption notification, user: " + user.getIdentifier());
diff --git a/services/core/java/com/android/server/logcat/LogcatManagerService.java b/services/core/java/com/android/server/logcat/LogcatManagerService.java
new file mode 100644
index 000000000000..ff6372aec3bd
--- /dev/null
+++ b/services/core/java/com/android/server/logcat/LogcatManagerService.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.logcat;
+
+import android.content.Context;
+import android.os.ILogd;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.logcat.ILogcatManagerService;
+import android.util.Slog;
+
+import com.android.server.SystemService;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+/**
+ * Service responsible for manage the access to Logcat.
+ */
+public final class LogcatManagerService extends SystemService {
+
+ private static final String TAG = "LogcatManagerService";
+ private final Context mContext;
+ private final BinderService mBinderService;
+ private final ExecutorService mThreadExecutor;
+ private ILogd mLogdService;
+
+ private final class BinderService extends ILogcatManagerService.Stub {
+ @Override
+ public void startThread(int uid, int gid, int pid, int fd) {
+ mThreadExecutor.execute(new LogdMonitor(uid, gid, pid, fd, true));
+ }
+
+ @Override
+ public void finishThread(int uid, int gid, int pid, int fd) {
+ // TODO This thread will be used to notify the AppOpsManager that
+ // the logd data access is finished.
+ mThreadExecutor.execute(new LogdMonitor(uid, gid, pid, fd, false));
+ }
+ }
+
+ private class LogdMonitor implements Runnable {
+
+ private final int mUid;
+ private final int mGid;
+ private final int mPid;
+ private final int mFd;
+ private final boolean mStart;
+
+ /**
+ * For starting a thread, the start value is true.
+ * For finishing a thread, the start value is false.
+ */
+ LogdMonitor(int uid, int gid, int pid, int fd, boolean start) {
+ mUid = uid;
+ mGid = gid;
+ mPid = pid;
+ mFd = fd;
+ mStart = start;
+ }
+
+ /**
+ * The current version grant the permission by default.
+ * And track the logd access.
+ * The next version will generate a prompt for users.
+ * The users decide whether the logd access is allowed.
+ */
+ @Override
+ public void run() {
+ if (mLogdService == null) {
+ LogcatManagerService.this.addLogdService();
+ }
+
+ if (mStart) {
+ try {
+ mLogdService.approve(mUid, mGid, mPid, mFd);
+ } catch (RemoteException ex) {
+ Slog.e(TAG, "Fails to call remote functions ", ex);
+ }
+ }
+ }
+ }
+
+ public LogcatManagerService(Context context) {
+ super(context);
+ mContext = context;
+ mBinderService = new BinderService();
+ mThreadExecutor = Executors.newCachedThreadPool();
+ }
+
+ @Override
+ public void onStart() {
+ try {
+ publishBinderService("logcat", mBinderService);
+ } catch (Throwable t) {
+ Slog.e(TAG, "Could not start the LogcatManagerService.", t);
+ }
+ }
+
+ private void addLogdService() {
+ mLogdService = ILogd.Stub.asInterface(ServiceManager.getService("logd"));
+ }
+
+}
diff --git a/services/core/java/com/android/server/logcat/OWNERS b/services/core/java/com/android/server/logcat/OWNERS
new file mode 100644
index 000000000000..9588fa9d1f8e
--- /dev/null
+++ b/services/core/java/com/android/server/logcat/OWNERS
@@ -0,0 +1,5 @@
+cbrubaker@google.com
+eunjeongshin@google.com
+jsharkey@google.com
+vishwath@google.com
+wenhaowang@google.com
diff --git a/services/core/java/com/android/server/media/BluetoothRouteProvider.java b/services/core/java/com/android/server/media/BluetoothRouteProvider.java
index ffc1aed4c672..91de9e559e13 100644
--- a/services/core/java/com/android/server/media/BluetoothRouteProvider.java
+++ b/services/core/java/com/android/server/media/BluetoothRouteProvider.java
@@ -344,10 +344,19 @@ class BluetoothRouteProvider {
}
private void addActiveRoute(BluetoothRouteInfo btRoute) {
+ if (btRoute == null) {
+ if (DEBUG) {
+ Log.d(TAG, " btRoute is null");
+ }
+ return;
+ }
if (DEBUG) {
Log.d(TAG, "Adding active route: " + btRoute.route);
}
- if (btRoute == null || mActiveRoutes.contains(btRoute)) {
+ if (mActiveRoutes.contains(btRoute)) {
+ if (DEBUG) {
+ Log.d(TAG, " btRoute is already added.");
+ }
return;
}
setRouteConnectionState(btRoute, STATE_CONNECTED);
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 9bc090fe46d4..e555c1356df7 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -63,7 +63,6 @@ import static android.net.INetd.FIREWALL_RULE_DENY;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
-import static android.net.NetworkIdentity.OEM_NONE;
import static android.net.NetworkPolicy.LIMIT_DISABLED;
import static android.net.NetworkPolicy.SNOOZE_NEVER;
import static android.net.NetworkPolicy.WARNING_DISABLED;
@@ -1498,13 +1497,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
for (int i = 0; i < mSubIdToSubscriberId.size(); i++) {
final int subId = mSubIdToSubscriberId.keyAt(i);
final String subscriberId = mSubIdToSubscriberId.valueAt(i);
- final NetworkIdentity probeIdent = new NetworkIdentity(TYPE_MOBILE,
- TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true,
- true, OEM_NONE);
- /* While OEM_NONE indicates "any non OEM managed network", OEM_NONE is meant to be a
- * placeholder value here. The probeIdent is matched against a NetworkTemplate which
- * should have its OEM managed value set to OEM_MANAGED_ALL, which will cause the
- * template to match probeIdent without regard to OEM managed status. */
+ final NetworkIdentity probeIdent = new NetworkIdentity.Builder()
+ .setType(TYPE_MOBILE)
+ .setSubscriberId(subscriberId)
+ .setMetered(true)
+ .setDefaultNetwork(true).build();
if (template.matches(probeIdent)) {
return subId;
}
@@ -1737,9 +1734,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
// find and update the carrier NetworkPolicy for this subscriber id
boolean policyUpdated = false;
- final NetworkIdentity probeIdent = new NetworkIdentity(TYPE_MOBILE,
- TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true, true,
- OEM_NONE);
+ final NetworkIdentity probeIdent = new NetworkIdentity.Builder()
+ .setType(TYPE_MOBILE)
+ .setSubscriberId(subscriberId)
+ .setMetered(true)
+ .setDefaultNetwork(true).build();
for (int i = mNetworkPolicy.size() - 1; i >= 0; i--) {
final NetworkTemplate template = mNetworkPolicy.keyAt(i);
if (template.matches(probeIdent)) {
@@ -1967,10 +1966,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
for (int i = 0; i < mSubIdToSubscriberId.size(); i++) {
final int subId = mSubIdToSubscriberId.keyAt(i);
final String subscriberId = mSubIdToSubscriberId.valueAt(i);
-
- final NetworkIdentity probeIdent = new NetworkIdentity(TYPE_MOBILE,
- TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true,
- true, OEM_NONE);
+ final NetworkIdentity probeIdent = new NetworkIdentity.Builder()
+ .setType(TYPE_MOBILE)
+ .setSubscriberId(subscriberId)
+ .setMetered(true)
+ .setDefaultNetwork(true).build();
// Template is matched when subscriber id matches.
if (template.matches(probeIdent)) {
matchingSubIds.add(subId);
@@ -2074,11 +2074,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
for (final NetworkStateSnapshot snapshot : snapshots) {
mNetIdToSubId.put(snapshot.getNetwork().getNetId(), parseSubId(snapshot));
- // Policies matched by NPMS only match by subscriber ID or by network ID. Thus subtype
- // in the object created here is never used and its value doesn't matter, so use
- // NETWORK_TYPE_UNKNOWN.
- final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, snapshot,
- true, TelephonyManager.NETWORK_TYPE_UNKNOWN /* subType */);
+ // Policies matched by NPMS only match by subscriber ID or by network ID.
+ final NetworkIdentity ident = new NetworkIdentity.Builder()
+ .setNetworkStateSnapshot(snapshot).setDefaultNetwork(true).build();
identified.put(snapshot, ident);
}
@@ -2275,9 +2273,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
@GuardedBy("mNetworkPoliciesSecondLock")
private boolean ensureActiveCarrierPolicyAL(int subId, String subscriberId) {
// Poke around to see if we already have a policy
- final NetworkIdentity probeIdent = new NetworkIdentity(TYPE_MOBILE,
- TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true, true,
- OEM_NONE);
+ final NetworkIdentity probeIdent = new NetworkIdentity.Builder()
+ .setType(TYPE_MOBILE)
+ .setSubscriberId(subscriberId)
+ .setMetered(true)
+ .setDefaultNetwork(true).build();
for (int i = mNetworkPolicy.size() - 1; i >= 0; i--) {
final NetworkTemplate template = mNetworkPolicy.keyAt(i);
if (template.matches(probeIdent)) {
@@ -2687,7 +2687,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
final List<WifiConfiguration> configs = wm.getConfiguredNetworks();
for (int i = 0; i < configs.size(); ++i) {
final WifiConfiguration config = configs.get(i);
- for (String key : config.getAllPersistableNetworkKeys()) {
+ for (String key : config.getAllNetworkKeys()) {
final Boolean metered = wifiNetworkKeys.get(key);
if (metered != null) {
Slog.d(TAG, "Found network " + key + "; upgrading metered hint");
@@ -3452,7 +3452,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
* {@link NetworkStatsProvider#onSetWarningAndLimit(String, long, long)}.
*/
@Override
- public void onStatsProviderWarningOrLimitReached() {
+ public void notifyStatsProviderWarningOrLimitReached() {
enforceAnyPermissionOf(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
// This API may be called before the system is ready.
synchronized (mNetworkPoliciesSecondLock) {
@@ -5078,7 +5078,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
// make sure stats are recorded frequently enough; we aim
// for 2MB threshold for 2GB/month rules.
final long persistThreshold = lowestRule / 1000;
- mNetworkStats.advisePersistThreshold(persistThreshold);
+ // TODO: Sync internal naming with the API surface.
+ mNetworkStats.setDefaultGlobalAlert(persistThreshold);
return true;
}
case MSG_UPDATE_INTERFACE_QUOTAS: {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 86b385b4d810..6b27321f856f 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -499,6 +499,7 @@ public class NotificationManagerService extends SystemService {
private IPlatformCompat mPlatformCompat;
private ShortcutHelper mShortcutHelper;
private PermissionHelper mPermissionHelper;
+ private UsageStatsManagerInternal mUsageStatsManagerInternal;
final IBinder mForegroundToken = new Binder();
private WorkerHandler mHandler;
@@ -2092,7 +2093,8 @@ public class NotificationManagerService extends SystemService {
UserManager userManager,
NotificationHistoryManager historyManager, StatsManager statsManager,
TelephonyManager telephonyManager, ActivityManagerInternal ami,
- MultiRateLimiter toastRateLimiter, PermissionHelper permissionHelper) {
+ MultiRateLimiter toastRateLimiter, PermissionHelper permissionHelper,
+ UsageStatsManagerInternal usageStatsManagerInternal) {
mHandler = handler;
Resources resources = getContext().getResources();
mMaxPackageEnqueueRate = Settings.Global.getFloat(getContext().getContentResolver(),
@@ -2110,6 +2112,7 @@ public class NotificationManagerService extends SystemService {
mPackageManagerClient = packageManagerClient;
mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
mPermissionPolicyInternal = LocalServices.getService(PermissionPolicyInternal.class);
+ mUsageStatsManagerInternal = usageStatsManagerInternal;
mAppOps = appOps;
mAppOpsService = iAppOps;
try {
@@ -2411,7 +2414,8 @@ public class NotificationManagerService extends SystemService {
LocalServices.getService(ActivityManagerInternal.class),
createToastRateLimiter(), new PermissionHelper(LocalServices.getService(
PermissionManagerServiceInternal.class), AppGlobals.getPackageManager(),
- AppGlobals.getPermissionManager(), mEnableAppSettingMigration));
+ AppGlobals.getPermissionManager(), mEnableAppSettingMigration),
+ LocalServices.getService(UsageStatsManagerInternal.class));
publishBinderService(Context.NOTIFICATION_SERVICE, mService, /* allowIsolated= */ false,
DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_NORMAL);
@@ -7259,6 +7263,8 @@ public class NotificationManagerService extends SystemService {
if (index < 0) {
mNotificationList.add(r);
mUsageStats.registerPostedByApp(r);
+ mUsageStatsManagerInternal.reportNotificationPosted(r.getSbn().getOpPkg(),
+ r.getSbn().getUser(), SystemClock.elapsedRealtime());
final boolean isInterruptive = isVisuallyInterruptive(null, r);
r.setInterruptive(isInterruptive);
r.setTextChanged(isInterruptive);
@@ -7266,6 +7272,8 @@ public class NotificationManagerService extends SystemService {
old = mNotificationList.get(index); // Potentially *changes* old
mNotificationList.set(index, r);
mUsageStats.registerUpdatedByApp(r, old);
+ mUsageStatsManagerInternal.reportNotificationUpdated(r.getSbn().getOpPkg(),
+ r.getSbn().getUser(), SystemClock.elapsedRealtime());
// Make sure we don't lose the foreground service state.
notification.flags |=
old.getNotification().flags & FLAG_FOREGROUND_SERVICE;
@@ -8748,6 +8756,8 @@ public class NotificationManagerService extends SystemService {
case REASON_APP_CANCEL:
case REASON_APP_CANCEL_ALL:
mUsageStats.registerRemovedByApp(r);
+ mUsageStatsManagerInternal.reportNotificationRemoved(r.getSbn().getOpPkg(),
+ r.getUser(), SystemClock.elapsedRealtime());
break;
}
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 5e333daed7d9..05f000c607d8 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -101,6 +101,8 @@ public class PreferencesHelper implements RankingConfig {
@VisibleForTesting
static final int NOTIFICATION_CHANNEL_COUNT_LIMIT = 50000;
+ @VisibleForTesting
+ static final int NOTIFICATION_CHANNEL_GROUP_COUNT_LIMIT = 50000;
private static final int NOTIFICATION_PREFERENCES_PULL_LIMIT = 1000;
private static final int NOTIFICATION_CHANNEL_PULL_LIMIT = 2000;
@@ -254,6 +256,7 @@ public class PreferencesHelper implements RankingConfig {
}
}
boolean skipWarningLogged = false;
+ boolean skipGroupWarningLogged = false;
boolean hasSAWPermission = false;
if (upgradeForBubbles && uid != UNKNOWN_UID) {
hasSAWPermission = mAppOps.noteOpNoThrow(
@@ -303,6 +306,14 @@ public class PreferencesHelper implements RankingConfig {
String tagName = parser.getName();
// Channel groups
if (TAG_GROUP.equals(tagName)) {
+ if (r.groups.size() >= NOTIFICATION_CHANNEL_GROUP_COUNT_LIMIT) {
+ if (!skipGroupWarningLogged) {
+ Slog.w(TAG, "Skipping further groups for " + r.pkg
+ + "; app has too many");
+ skipGroupWarningLogged = true;
+ }
+ continue;
+ }
String id = parser.getAttributeValue(null, ATT_ID);
CharSequence groupName = parser.getAttributeValue(null,
ATT_NAME);
@@ -867,6 +878,9 @@ public class PreferencesHelper implements RankingConfig {
}
if (fromTargetApp) {
group.setBlocked(false);
+ if (r.groups.size() >= NOTIFICATION_CHANNEL_GROUP_COUNT_LIMIT) {
+ throw new IllegalStateException("Limit exceed; cannot create more groups");
+ }
}
final NotificationChannelGroup oldGroup = r.groups.get(group.getId());
if (oldGroup != null) {
diff --git a/services/core/java/com/android/server/om/IdmapDaemon.java b/services/core/java/com/android/server/om/IdmapDaemon.java
index c9e564a0ce9d..8e944b7a965d 100644
--- a/services/core/java/com/android/server/om/IdmapDaemon.java
+++ b/services/core/java/com/android/server/om/IdmapDaemon.java
@@ -37,13 +37,14 @@ import com.android.server.FgThread;
import java.io.File;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
/**
- * To prevent idmap2d from continuously running, the idmap daemon will terminate after 10
- * seconds without a transaction.
+ * To prevent idmap2d from continuously running, the idmap daemon will terminate after 10 seconds
+ * without a transaction.
**/
class IdmapDaemon {
// The amount of time in milliseconds to wait after a transaction to the idmap service is made
@@ -67,11 +68,14 @@ class IdmapDaemon {
* to the service is open.
**/
private class Connection implements AutoCloseable {
+ @Nullable
+ private final IIdmap2 mIdmap2;
private boolean mOpened = true;
- private Connection() {
+ private Connection(IIdmap2 idmap2) {
synchronized (mIdmapToken) {
mOpenedCount.incrementAndGet();
+ mIdmap2 = idmap2;
}
}
@@ -102,6 +106,11 @@ class IdmapDaemon {
}, mIdmapToken, SERVICE_TIMEOUT_MS);
}
}
+
+ @Nullable
+ public IIdmap2 getIdmap2() {
+ return mIdmap2;
+ }
}
static IdmapDaemon getInstance() {
@@ -115,14 +124,29 @@ class IdmapDaemon {
@Nullable String overlayName, int policies, boolean enforce, int userId)
throws TimeoutException, RemoteException {
try (Connection c = connect()) {
- return mService.createIdmap(targetPath, overlayPath, TextUtils.emptyIfNull(overlayName),
+ final IIdmap2 idmap2 = c.getIdmap2();
+ if (idmap2 == null) {
+ Slog.w(TAG, "idmap2d service is not ready for createIdmap(\"" + targetPath
+ + "\", \"" + overlayPath + "\", \"" + overlayName + "\", " + policies + ", "
+ + enforce + ", " + userId + ")");
+ return null;
+ }
+
+ return idmap2.createIdmap(targetPath, overlayPath, TextUtils.emptyIfNull(overlayName),
policies, enforce, userId);
}
}
boolean removeIdmap(String overlayPath, int userId) throws TimeoutException, RemoteException {
try (Connection c = connect()) {
- return mService.removeIdmap(overlayPath, userId);
+ final IIdmap2 idmap2 = c.getIdmap2();
+ if (idmap2 == null) {
+ Slog.w(TAG, "idmap2d service is not ready for removeIdmap(\"" + overlayPath
+ + "\", " + userId + ")");
+ return false;
+ }
+
+ return idmap2.removeIdmap(overlayPath, userId);
}
}
@@ -130,14 +154,29 @@ class IdmapDaemon {
@Nullable String overlayName, int policies, boolean enforce, int userId)
throws Exception {
try (Connection c = connect()) {
- return mService.verifyIdmap(targetPath, overlayPath, TextUtils.emptyIfNull(overlayName),
+ final IIdmap2 idmap2 = c.getIdmap2();
+ if (idmap2 == null) {
+ Slog.w(TAG, "idmap2d service is not ready for verifyIdmap(\"" + targetPath
+ + "\", \"" + overlayPath + "\", \"" + overlayName + "\", " + policies + ", "
+ + enforce + ", " + userId + ")");
+ return false;
+ }
+
+ return idmap2.verifyIdmap(targetPath, overlayPath, TextUtils.emptyIfNull(overlayName),
policies, enforce, userId);
}
}
boolean idmapExists(String overlayPath, int userId) {
try (Connection c = connect()) {
- return new File(mService.getIdmapPath(overlayPath, userId)).isFile();
+ final IIdmap2 idmap2 = c.getIdmap2();
+ if (idmap2 == null) {
+ Slog.w(TAG, "idmap2d service is not ready for idmapExists(\"" + overlayPath
+ + "\", " + userId + ")");
+ return false;
+ }
+
+ return new File(idmap2.getIdmapPath(overlayPath, userId)).isFile();
} catch (Exception e) {
Slog.wtf(TAG, "failed to check if idmap exists for " + overlayPath, e);
return false;
@@ -146,7 +185,13 @@ class IdmapDaemon {
FabricatedOverlayInfo createFabricatedOverlay(@NonNull FabricatedOverlayInternal overlay) {
try (Connection c = connect()) {
- return mService.createFabricatedOverlay(overlay);
+ final IIdmap2 idmap2 = c.getIdmap2();
+ if (idmap2 == null) {
+ Slog.w(TAG, "idmap2d service is not ready for createFabricatedOverlay()");
+ return null;
+ }
+
+ return idmap2.createFabricatedOverlay(overlay);
} catch (Exception e) {
Slog.wtf(TAG, "failed to fabricate overlay " + overlay, e);
return null;
@@ -155,7 +200,14 @@ class IdmapDaemon {
boolean deleteFabricatedOverlay(@NonNull String path) {
try (Connection c = connect()) {
- return mService.deleteFabricatedOverlay(path);
+ final IIdmap2 idmap2 = c.getIdmap2();
+ if (idmap2 == null) {
+ Slog.w(TAG, "idmap2d service is not ready for deleteFabricatedOverlay(\"" + path
+ + "\")");
+ return false;
+ }
+
+ return idmap2.deleteFabricatedOverlay(path);
} catch (Exception e) {
Slog.wtf(TAG, "failed to delete fabricated overlay '" + path + "'", e);
return false;
@@ -164,10 +216,18 @@ class IdmapDaemon {
synchronized List<FabricatedOverlayInfo> getFabricatedOverlayInfos() {
final ArrayList<FabricatedOverlayInfo> allInfos = new ArrayList<>();
- try (Connection c = connect()) {
- mService.acquireFabricatedOverlayIterator();
+ Connection c = null;
+ try {
+ c = connect();
+ final IIdmap2 service = c.getIdmap2();
+ if (service == null) {
+ Slog.w(TAG, "idmap2d service is not ready for getFabricatedOverlayInfos()");
+ return Collections.emptyList();
+ }
+
+ service.acquireFabricatedOverlayIterator();
List<FabricatedOverlayInfo> infos;
- while (!(infos = mService.nextFabricatedOverlayInfos()).isEmpty()) {
+ while (!(infos = service.nextFabricatedOverlayInfos()).isEmpty()) {
allInfos.addAll(infos);
}
return allInfos;
@@ -175,17 +235,26 @@ class IdmapDaemon {
Slog.wtf(TAG, "failed to get all fabricated overlays", e);
} finally {
try {
- mService.releaseFabricatedOverlayIterator();
+ if (c.getIdmap2() != null) {
+ c.getIdmap2().releaseFabricatedOverlayIterator();
+ }
} catch (RemoteException e) {
// ignore
}
+ c.close();
}
return allInfos;
}
String dumpIdmap(@NonNull String overlayPath) {
try (Connection c = connect()) {
- String dump = mService.dumpIdmap(overlayPath);
+ final IIdmap2 service = c.getIdmap2();
+ if (service == null) {
+ final String dumpText = "idmap2d service is not ready for dumpIdmap()";
+ Slog.w(TAG, dumpText);
+ return dumpText;
+ }
+ String dump = service.dumpIdmap(overlayPath);
return TextUtils.nullIfEmpty(dump);
} catch (Exception e) {
Slog.wtf(TAG, "failed to dump idmap", e);
@@ -193,8 +262,16 @@ class IdmapDaemon {
}
}
+ @Nullable
private IBinder getIdmapService() throws TimeoutException, RemoteException {
- SystemService.start(IDMAP_DAEMON);
+ try {
+ SystemService.start(IDMAP_DAEMON);
+ } catch (RuntimeException e) {
+ if (e.getMessage().contains("failed to set system property")) {
+ Slog.w(TAG, "Failed to enable idmap2 daemon", e);
+ return null;
+ }
+ }
final long endMillis = SystemClock.elapsedRealtime() + SERVICE_CONNECT_TIMEOUT_MS;
while (SystemClock.elapsedRealtime() <= endMillis) {
@@ -226,17 +303,23 @@ class IdmapDaemon {
}
}
+ @NonNull
private Connection connect() throws TimeoutException, RemoteException {
synchronized (mIdmapToken) {
FgThread.getHandler().removeCallbacksAndMessages(mIdmapToken);
if (mService != null) {
// Not enough time has passed to stop the idmap service. Reuse the existing
// interface.
- return new Connection();
+ return new Connection(mService);
+ }
+
+ IBinder binder = getIdmapService();
+ if (binder == null) {
+ return new Connection(null);
}
- mService = IIdmap2.Stub.asInterface(getIdmapService());
- return new Connection();
+ mService = IIdmap2.Stub.asInterface(binder);
+ return new Connection(mService);
}
}
}
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index 6f10a6bbb9ca..2e9ad50f23f6 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -45,6 +45,7 @@ import android.os.Trace;
import android.sysprop.ApexProperties;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.PrintWriterPrinter;
import android.util.Singleton;
import android.util.Slog;
import android.util.SparseArray;
@@ -1164,6 +1165,10 @@ public abstract class ApexManager {
ipw.println("Path: " + pi.applicationInfo.sourceDir);
ipw.println("IsActive: " + isActive(pi));
ipw.println("IsFactory: " + isFactory(pi));
+ ipw.println("ApplicationInfo: ");
+ ipw.increaseIndent();
+ pi.applicationInfo.dump(new PrintWriterPrinter(ipw), "");
+ ipw.decreaseIndent();
ipw.decreaseIndent();
}
ipw.decreaseIndent();
diff --git a/services/core/java/com/android/server/pm/AppDataHelper.java b/services/core/java/com/android/server/pm/AppDataHelper.java
index a66af3cf7603..4b999e9fac7f 100644
--- a/services/core/java/com/android/server/pm/AppDataHelper.java
+++ b/services/core/java/com/android/server/pm/AppDataHelper.java
@@ -24,7 +24,6 @@ import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.pm.PackageManager;
-import com.android.server.pm.pkg.SELinuxUtil;
import android.content.pm.UserInfo;
import android.os.CreateAppDataArgs;
import android.os.Environment;
@@ -35,6 +34,9 @@ import android.os.UserHandle;
import android.os.storage.StorageManager;
import android.os.storage.StorageManagerInternal;
import android.os.storage.VolumeInfo;
+import android.security.AndroidKeyStoreMaintenance;
+import android.system.keystore2.Domain;
+import android.system.keystore2.KeyDescriptor;
import android.text.TextUtils;
import android.util.Log;
import android.util.Slog;
@@ -46,6 +48,7 @@ import com.android.server.SystemServerInitThreadPool;
import com.android.server.pm.dex.ArtManagerService;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
+import com.android.server.pm.pkg.SELinuxUtil;
import dalvik.system.VMRuntime;
@@ -156,8 +159,7 @@ final class AppDataHelper {
* <ul>
* <li>If previousAppId < 0, app data will be migrated to the new app ID
* <li>If previousAppId == 0, no migration will happen and data will be wiped and recreated
- * <li>If previousAppId > 0, it will migrate all data owned by previousAppId
- * to the new app ID
+ * <li>If previousAppId > 0, app data owned by previousAppId will be migrated to the new app ID
* </ul>
*/
private @NonNull CompletableFuture<?> prepareAppData(@NonNull Installer.Batch batch,
@@ -476,13 +478,9 @@ final class AppDataHelper {
} else if (!ps.getInstalled(userId)) {
throw new PackageManagerException(
"Package " + packageName + " not installed for user " + userId);
- } else if (ps.getPkg() == null) {
- throw new PackageManagerException("Package " + packageName + " is not parsed yet");
- } else {
- if (!shouldHaveAppStorage(ps.getPkg())) {
- throw new PackageManagerException(
- "Package " + packageName + " shouldn't have storage");
- }
+ } else if (ps.getPkg() != null && !shouldHaveAppStorage(ps.getPkg())) {
+ throw new PackageManagerException(
+ "Package " + packageName + " shouldn't have storage");
}
}
}
@@ -549,6 +547,22 @@ final class AppDataHelper {
return prepareAppDataFuture;
}
+ public void migrateKeyStoreData(int previousAppId, int appId) {
+ for (int userId : mPm.resolveUserIds(UserHandle.USER_ALL)) {
+ int srcUid = UserHandle.getUid(userId, previousAppId);
+ int destUid = UserHandle.getUid(userId, appId);
+ final KeyDescriptor[] keys = AndroidKeyStoreMaintenance.listEntries(Domain.APP, srcUid);
+ if (keys == null) continue;
+ for (final KeyDescriptor key : keys) {
+ KeyDescriptor dest = new KeyDescriptor();
+ dest.domain = Domain.APP;
+ dest.nspace = destUid;
+ dest.alias = key.alias;
+ AndroidKeyStoreMaintenance.migrateKeyNamespace(key, dest);
+ }
+ }
+ }
+
void clearAppDataLIF(AndroidPackage pkg, int userId, int flags) {
if (pkg == null) {
return;
@@ -633,4 +647,18 @@ final class AppDataHelper {
pkg.getProperties().get(PackageManager.PROPERTY_NO_APP_DATA_STORAGE);
return noAppDataProp == null || !noAppDataProp.getBoolean();
}
+
+ /**
+ * Remove entries from the keystore daemon. Will only remove if the {@code appId} is valid.
+ */
+ public void clearKeystoreData(int userId, int appId) {
+ if (appId < 0) {
+ return;
+ }
+
+ for (int realUserId : mPm.resolveUserIds(userId)) {
+ AndroidKeyStoreMaintenance.clearNamespace(
+ Domain.APP, UserHandle.getUid(realUserId, appId));
+ }
+ }
}
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index 2aa0e0174d0d..69c475a05a93 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -20,6 +20,7 @@ import static android.Manifest.permission.DELETE_PACKAGES;
import static android.Manifest.permission.INSTALL_PACKAGES;
import static android.Manifest.permission.REQUEST_DELETE_PACKAGES;
import static android.Manifest.permission.SET_HARMFUL_APP_WARNINGS;
+import static android.content.ContentProvider.isAuthorityRedirectedForCloneProfile;
import static android.content.Intent.ACTION_MAIN;
import static android.content.Intent.CATEGORY_DEFAULT;
import static android.content.Intent.CATEGORY_HOME;
@@ -92,14 +93,6 @@ import android.content.pm.SigningDetails;
import android.content.pm.SigningInfo;
import android.content.pm.UserInfo;
import android.content.pm.VersionedPackage;
-import com.android.server.pm.pkg.parsing.PackageInfoWithoutStateUtils;
-import com.android.server.pm.pkg.component.ParsedActivity;
-import com.android.server.pm.pkg.component.ParsedInstrumentation;
-import com.android.server.pm.pkg.component.ParsedIntentInfo;
-import com.android.server.pm.pkg.component.ParsedMainComponent;
-import com.android.server.pm.pkg.component.ParsedProvider;
-import com.android.server.pm.pkg.component.ParsedService;
-import com.android.server.pm.pkg.PackageUserStateUtils;
import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
@@ -142,6 +135,14 @@ import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.pm.pkg.PackageStateUtils;
import com.android.server.pm.pkg.PackageUserState;
import com.android.server.pm.pkg.PackageUserStateInternal;
+import com.android.server.pm.pkg.PackageUserStateUtils;
+import com.android.server.pm.pkg.component.ParsedActivity;
+import com.android.server.pm.pkg.component.ParsedInstrumentation;
+import com.android.server.pm.pkg.component.ParsedIntentInfo;
+import com.android.server.pm.pkg.component.ParsedMainComponent;
+import com.android.server.pm.pkg.component.ParsedProvider;
+import com.android.server.pm.pkg.component.ParsedService;
+import com.android.server.pm.pkg.parsing.PackageInfoWithoutStateUtils;
import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
import com.android.server.pm.verify.domain.DomainVerificationUtils;
import com.android.server.uri.UriGrantsManagerInternal;
@@ -350,7 +351,6 @@ public class ComputerEngine implements Computer {
private final CompilerStats mCompilerStats;
private final BackgroundDexOptService mBackgroundDexOptService;
private final PackageManagerInternal.ExternalSourcesPolicy mExternalSourcesPolicy;
- private final ProtectedPackages mProtectedPackages;
// PackageManagerService attributes that are primitives are referenced through the
// pms object directly. Primitives are the only attributes so referenced.
@@ -402,7 +402,6 @@ public class ComputerEngine implements Computer {
mCompilerStats = args.service.mCompilerStats;
mBackgroundDexOptService = args.service.mBackgroundDexOptService;
mExternalSourcesPolicy = args.service.mExternalSourcesPolicy;
- mProtectedPackages = args.service.mProtectedPackages;
// Used to reference PMS attributes that are primitives and which are not
// updated under control of the PMS lock.
@@ -4619,8 +4618,24 @@ public class ComputerEngine implements Computer {
}
}
if (!checkedGrants) {
- enforceCrossUserPermission(callingUid, userId, false, false, "resolveContentProvider");
+ boolean enforceCrossUser = true;
+
+ if (isAuthorityRedirectedForCloneProfile(name)) {
+ final UserManagerInternal umInternal = mInjector.getUserManagerInternal();
+
+ UserInfo userInfo = umInternal.getUserInfo(UserHandle.getUserId(callingUid));
+ if (userInfo != null && userInfo.isCloneProfile()
+ && userInfo.profileGroupId == userId) {
+ enforceCrossUser = false;
+ }
+ }
+
+ if (enforceCrossUser) {
+ enforceCrossUserPermission(callingUid, userId, false, false,
+ "resolveContentProvider");
+ }
}
+
if (providerInfo == null) {
return null;
}
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index 9a80a4e558c4..48689a8da335 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -485,8 +485,7 @@ final class DeletePackageHelper {
mAppDataHelper.destroyAppDataLIF(pkg, nextUserId,
FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL);
}
- PackageManagerService.removeKeystoreDataIfNeeded(mUserManagerInternal, nextUserId,
- ps.getAppId());
+ mAppDataHelper.clearKeystoreData(nextUserId, ps.getAppId());
preferredActivityHelper.clearPackagePreferredActivities(ps.getPackageName(),
nextUserId);
mPm.mDomainVerificationManager.clearPackageForUser(ps.getPackageName(), nextUserId);
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 6a5d76bc248b..80699ac5dd82 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -2245,6 +2245,17 @@ final class InstallPackageHelper {
if (reconciledPkg.mScanResult.needsNewAppId()) {
// Only set previousAppId if the app is migrating out of shared UID
previousAppId = reconciledPkg.mScanResult.mPreviousAppId;
+
+ if (pkg.shouldInheritKeyStoreKeys()) {
+ // Migrate keystore data
+ mAppDataHelper.migrateKeyStoreData(
+ previousAppId, reconciledPkg.mPkgSetting.getAppId());
+ }
+
+ if (reconciledPkg.mInstallResult.mRemovedInfo.mRemovedAppId == previousAppId) {
+ // If the previous app ID is removed, clear the keys
+ mAppDataHelper.clearKeystoreData(UserHandle.USER_ALL, previousAppId);
+ }
}
mAppDataHelper.prepareAppDataPostCommitLIF(pkg, previousAppId);
if (reconciledPkg.mPrepareResult.mClearCodeCache) {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 1f10d77086d3..ccc375ff85f2 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -16,6 +16,8 @@
package com.android.server.pm;
+import static android.app.admin.DevicePolicyResources.Strings.Core.PACKAGE_DELETED_BY_DO;
+
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
import static org.xmlpull.v1.XmlPullParser.START_TAG;
@@ -29,6 +31,7 @@ import android.app.Notification;
import android.app.NotificationManager;
import android.app.PackageDeleteObserver;
import android.app.admin.DevicePolicyEventLogger;
+import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyManagerInternal;
import android.content.Context;
import android.content.Intent;
@@ -1312,7 +1315,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
mPackageName = packageName;
if (showNotification) {
mNotification = buildSuccessNotification(mContext,
- mContext.getResources().getString(R.string.package_deleted_device_owner),
+ getDeviceOwnerDeletedPackageMsg(),
packageName,
userId);
} else {
@@ -1320,6 +1323,12 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
}
}
+ private String getDeviceOwnerDeletedPackageMsg() {
+ DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
+ return dpm.getString(PACKAGE_DELETED_BY_DO,
+ () -> mContext.getString(R.string.package_updated_device_owner));
+ }
+
@Override
public void onUserActionRequired(Intent intent) {
if (mTarget == null) {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index e0f1b0b44cf8..d9ade967d0b4 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -17,6 +17,8 @@
package com.android.server.pm;
import static android.app.AppOpsManager.MODE_DEFAULT;
+import static android.app.admin.DevicePolicyResources.Strings.Core.PACKAGE_INSTALLED_BY_DO;
+import static android.app.admin.DevicePolicyResources.Strings.Core.PACKAGE_UPDATED_BY_DO;
import static android.content.pm.DataLoaderType.INCREMENTAL;
import static android.content.pm.DataLoaderType.STREAMING;
import static android.content.pm.PackageInstaller.LOCATION_DATA_APP;
@@ -56,6 +58,7 @@ import android.app.AppOpsManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.admin.DevicePolicyEventLogger;
+import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyManagerInternal;
import android.content.ComponentName;
import android.content.Context;
@@ -91,7 +94,6 @@ import android.content.pm.dex.DexMetadataHelper;
import android.content.pm.parsing.ApkLite;
import android.content.pm.parsing.ApkLiteParseUtils;
import android.content.pm.parsing.PackageLite;
-import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
import android.content.pm.parsing.result.ParseResult;
import android.content.pm.parsing.result.ParseTypeImpl;
import android.graphics.Bitmap;
@@ -156,6 +158,7 @@ import com.android.server.pm.Installer.InstallerException;
import com.android.server.pm.dex.DexManager;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
import libcore.io.IoUtils;
import libcore.util.EmptyArray;
@@ -4336,9 +4339,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
if (INSTALL_SUCCEEDED == returnCode && showNotification) {
boolean update = (extras != null) && extras.getBoolean(Intent.EXTRA_REPLACING);
Notification notification = PackageInstallerService.buildSuccessNotification(context,
- context.getResources()
- .getString(update ? R.string.package_updated_device_owner :
- R.string.package_installed_device_owner),
+ getDeviceOwnerInstalledPackageMsg(context, update),
basePackageName,
userId);
if (notification != null) {
@@ -4370,6 +4371,15 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
+ private static String getDeviceOwnerInstalledPackageMsg(Context context, boolean update) {
+ DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
+ return update
+ ? dpm.getString(PACKAGE_UPDATED_BY_DO,
+ () -> context.getString(R.string.package_updated_device_owner))
+ : dpm.getString(PACKAGE_INSTALLED_BY_DO,
+ () -> context.getString(R.string.package_installed_device_owner));
+ }
+
/**
* This method doesn't change internal states and is safe to call outside the lock.
*/
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 13f91e0dca62..e00f4f591d54 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -167,7 +167,6 @@ import android.permission.PermissionManager;
import android.provider.DeviceConfig;
import android.provider.Settings.Global;
import android.provider.Settings.Secure;
-import android.security.KeyStore;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.ArrayMap;
@@ -234,8 +233,6 @@ import com.android.server.pm.pkg.PackageState;
import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.pm.pkg.PackageStateUtils;
import com.android.server.pm.pkg.PackageUserState;
-import com.android.server.pm.pkg.PackageUserStateInternal;
-import com.android.server.pm.pkg.SuspendParams;
import com.android.server.pm.pkg.component.ParsedInstrumentation;
import com.android.server.pm.pkg.component.ParsedMainComponent;
import com.android.server.pm.pkg.mutate.PackageStateMutator;
@@ -291,7 +288,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import java.util.function.Function;
-import java.util.function.Predicate;
/**
* Keep track of all those APKs everywhere.
@@ -954,6 +950,7 @@ public class PackageManagerService extends IPackageManager.Stub
final @Nullable String mRetailDemoPackage;
final @Nullable String mOverlayConfigSignaturePackage;
final @Nullable String mRecentsPackage;
+ final @Nullable String mAmbientContextDetectionPackage;
@GuardedBy("mLock")
private final PackageUsage mPackageUsage = new PackageUsage();
@@ -970,6 +967,7 @@ public class PackageManagerService extends IPackageManager.Stub
private final PreferredActivityHelper mPreferredActivityHelper;
private final ResolveIntentHelper mResolveIntentHelper;
private final DexOptHelper mDexOptHelper;
+ private final SuspendPackageHelper mSuspendPackageHelper;
/**
* Invalidate the package info cache, which includes updating the cached computer.
@@ -1671,6 +1669,7 @@ public class PackageManagerService extends IPackageManager.Stub
mSystemTextClassifierPackageName = testParams.systemTextClassifierPackage;
mRetailDemoPackage = testParams.retailDemoPackage;
mRecentsPackage = testParams.recentsPackage;
+ mAmbientContextDetectionPackage = testParams.ambientContextDetectionPackage;
mConfiguratorPackage = testParams.configuratorPackage;
mAppPredictionServicePackage = testParams.appPredictionServicePackage;
mIncidentReportApproverPackage = testParams.incidentReportApproverPackage;
@@ -1705,6 +1704,7 @@ public class PackageManagerService extends IPackageManager.Stub
mPreferredActivityHelper = testParams.preferredActivityHelper;
mResolveIntentHelper = testParams.resolveIntentHelper;
mDexOptHelper = testParams.dexOptHelper;
+ mSuspendPackageHelper = testParams.suspendPackageHelper;
mSharedLibraries.setDeletePackageHelper(mDeletePackageHelper);
invalidatePackageInfoCache();
@@ -1851,6 +1851,8 @@ public class PackageManagerService extends IPackageManager.Stub
mPreferredActivityHelper = new PreferredActivityHelper(this);
mResolveIntentHelper = new ResolveIntentHelper(this, mPreferredActivityHelper);
mDexOptHelper = new DexOptHelper(this);
+ mSuspendPackageHelper = new SuspendPackageHelper(this, mInjector, mBroadcastHelper,
+ mProtectedPackages);
synchronized (mLock) {
// Create the computer as soon as the state objects have been installed. The
@@ -1995,6 +1997,7 @@ public class PackageManagerService extends IPackageManager.Stub
mRetailDemoPackage = getRetailDemoPackageName();
mOverlayConfigSignaturePackage = getOverlayConfigSignaturePackageName();
mRecentsPackage = getRecentsPackageName();
+ mAmbientContextDetectionPackage = getAmbientContextDetectionPackageName();
// Now that we know all of the shared libraries, update all clients to have
// the correct library paths.
@@ -2682,7 +2685,7 @@ public class PackageManagerService extends IPackageManager.Stub
return mComputer.getPackageUid(packageName, flags, userId);
}
- private int getPackageUidInternal(String packageName,
+ int getPackageUidInternal(String packageName,
@PackageManager.PackageInfoFlagsBits long flags, int userId, int callingUid) {
return mComputer.getPackageUidInternal(packageName, flags, userId, callingUid);
}
@@ -4294,51 +4297,6 @@ public class PackageManagerService extends IPackageManager.Stub
info.sendPackageRemovedBroadcasts(true /*killApp*/, false /*removedBySystem*/);
}
- @VisibleForTesting(visibility = Visibility.PRIVATE)
- void sendPackagesSuspendedForUser(String intent, String[] pkgList, int[] uidList, int userId) {
- final List<List<String>> pkgsToSend = new ArrayList(pkgList.length);
- final List<IntArray> uidsToSend = new ArrayList(pkgList.length);
- final List<SparseArray<int[]>> allowListsToSend = new ArrayList(pkgList.length);
- final int[] userIds = new int[] {userId};
- // Get allow lists for the pkg in the pkgList. Merge into the existed pkgs and uids if
- // allow lists are the same.
- for (int i = 0; i < pkgList.length; i++) {
- final String pkgName = pkgList[i];
- final int uid = uidList[i];
- SparseArray<int[]> allowList = mAppsFilter.getVisibilityAllowList(
- getPackageStateInternal(pkgName, Process.SYSTEM_UID),
- userIds, getPackageStates());
- if (allowList == null) {
- allowList = new SparseArray<>(0);
- }
- boolean merged = false;
- for (int j = 0; j < allowListsToSend.size(); j++) {
- if (Arrays.equals(allowListsToSend.get(j).get(userId), allowList.get(userId))) {
- pkgsToSend.get(j).add(pkgName);
- uidsToSend.get(j).add(uid);
- merged = true;
- break;
- }
- }
- if (!merged) {
- pkgsToSend.add(new ArrayList<>(Arrays.asList(pkgName)));
- uidsToSend.add(IntArray.wrap(new int[] {uid}));
- allowListsToSend.add(allowList);
- }
- }
-
- for (int i = 0; i < pkgsToSend.size(); i++) {
- final Bundle extras = new Bundle(3);
- extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST,
- pkgsToSend.get(i).toArray(new String[pkgsToSend.get(i).size()]));
- extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidsToSend.get(i).toArray());
- final SparseArray<int[]> allowList = allowListsToSend.get(i).size() == 0
- ? null : allowListsToSend.get(i);
- sendPackageBroadcast(intent, null, extras, Intent.FLAG_RECEIVER_REGISTERED_ONLY, null,
- null, userIds, null, allowList, null);
- }
- }
-
/**
* Returns true if application is not found or there was an error. Otherwise it returns
* the hidden state of the package for the given user.
@@ -4381,7 +4339,8 @@ public class PackageManagerService extends IPackageManager.Stub
+ userId);
}
Objects.requireNonNull(packageNames, "packageNames cannot be null");
- if (restrictionFlags != 0 && !isSuspendAllowedForUser(userId)) {
+ if (restrictionFlags != 0
+ && !mSuspendPackageHelper.isSuspendAllowedForUser(userId, callingUid)) {
Slog.w(TAG, "Cannot restrict packages due to restrictions on user " + userId);
return packageNames;
}
@@ -4389,8 +4348,9 @@ public class PackageManagerService extends IPackageManager.Stub
final List<String> changedPackagesList = new ArrayList<>(packageNames.length);
final IntArray changedUids = new IntArray(packageNames.length);
final List<String> unactionedPackages = new ArrayList<>(packageNames.length);
- final boolean[] canRestrict = (restrictionFlags != 0) ? canSuspendPackageForUserInternal(
- packageNames, userId) : null;
+ final boolean[] canRestrict = (restrictionFlags != 0)
+ ? mSuspendPackageHelper.canSuspendPackageForUser(packageNames, userId, callingUid)
+ : null;
for (int i = 0; i < packageNames.length; i++) {
final String packageName = packageNames[i];
@@ -4469,84 +4429,8 @@ public class PackageManagerService extends IPackageManager.Stub
final int callingUid = Binder.getCallingUid();
enforceCanSetPackagesSuspendedAsUser(callingPackage, callingUid, userId,
"setPackagesSuspendedAsUser");
-
- if (ArrayUtils.isEmpty(packageNames)) {
- return packageNames;
- }
- if (suspended && !isSuspendAllowedForUser(userId)) {
- Slog.w(TAG, "Cannot suspend due to restrictions on user " + userId);
- return packageNames;
- }
-
- final List<String> changedPackagesList = new ArrayList<>(packageNames.length);
- final IntArray changedUids = new IntArray(packageNames.length);
- final List<String> modifiedPackagesList = new ArrayList<>(packageNames.length);
- final IntArray modifiedUids = new IntArray(packageNames.length);
- final List<String> unactionedPackages = new ArrayList<>(packageNames.length);
- final boolean[] canSuspend = suspended ? canSuspendPackageForUserInternal(packageNames,
- userId) : null;
-
- for (int i = 0; i < packageNames.length; i++) {
- final String packageName = packageNames[i];
- if (callingPackage.equals(packageName)) {
- Slog.w(TAG, "Calling package: " + callingPackage + " trying to "
- + (suspended ? "" : "un") + "suspend itself. Ignoring");
- unactionedPackages.add(packageName);
- continue;
- }
- final PackageSetting pkgSetting;
- synchronized (mLock) {
- pkgSetting = mSettings.getPackageLPr(packageName);
- if (pkgSetting == null
- || shouldFilterApplication(pkgSetting, callingUid, userId)) {
- Slog.w(TAG, "Could not find package setting for package: " + packageName
- + ". Skipping suspending/un-suspending.");
- unactionedPackages.add(packageName);
- continue;
- }
- }
- if (canSuspend != null && !canSuspend[i]) {
- unactionedPackages.add(packageName);
- continue;
- }
- final boolean packageUnsuspended;
- final boolean packageModified;
- synchronized (mLock) {
- if (suspended) {
- packageModified = pkgSetting.addOrUpdateSuspension(callingPackage,
- dialogInfo, appExtras, launcherExtras, userId);
- } else {
- packageModified = pkgSetting.removeSuspension(callingPackage, userId);
- }
- packageUnsuspended = !suspended && !pkgSetting.getSuspended(userId);
- }
- if (suspended || packageUnsuspended) {
- changedPackagesList.add(packageName);
- changedUids.add(UserHandle.getUid(userId, pkgSetting.getAppId()));
- }
- if (packageModified) {
- modifiedPackagesList.add(packageName);
- modifiedUids.add(UserHandle.getUid(userId, pkgSetting.getAppId()));
- }
- }
-
- if (!changedPackagesList.isEmpty()) {
- final String[] changedPackages = changedPackagesList.toArray(new String[0]);
- sendPackagesSuspendedForUser(
- suspended ? Intent.ACTION_PACKAGES_SUSPENDED
- : Intent.ACTION_PACKAGES_UNSUSPENDED,
- changedPackages, changedUids.toArray(), userId);
- sendMyPackageSuspendedOrUnsuspended(changedPackages, suspended, userId);
- synchronized (mLock) {
- scheduleWritePackageRestrictionsLocked(userId);
- }
- }
- // Send the suspension changed broadcast to ensure suspension state is not stale.
- if (!modifiedPackagesList.isEmpty()) {
- sendPackagesSuspendedForUser(Intent.ACTION_PACKAGES_SUSPENSION_CHANGED,
- modifiedPackagesList.toArray(new String[0]), modifiedUids.toArray(), userId);
- }
- return unactionedPackages.toArray(new String[0]);
+ return mSuspendPackageHelper.setPackagesSuspended(packageNames, suspended, appExtras,
+ launcherExtras, dialogInfo, callingPackage, userId, callingUid);
}
@Override
@@ -4556,56 +4440,8 @@ public class PackageManagerService extends IPackageManager.Stub
throw new SecurityException("Calling package " + packageName
+ " does not belong to calling uid " + callingUid);
}
- return getSuspendedPackageAppExtrasInternal(packageName, userId);
- }
-
- private Bundle getSuspendedPackageAppExtrasInternal(String packageName, int userId) {
- final PackageStateInternal ps = getPackageStateInternal(packageName);
- if (ps == null) {
- return null;
- }
- final PackageUserStateInternal pus = ps.getUserStateOrDefault(userId);
- final Bundle allExtras = new Bundle();
- if (pus.isSuspended()) {
- for (int i = 0; i < pus.getSuspendParams().size(); i++) {
- final SuspendParams params = pus.getSuspendParams().valueAt(i);
- if (params != null && params.getAppExtras() != null) {
- allExtras.putAll(params.getAppExtras());
- }
- }
- }
- return (allExtras.size() > 0) ? allExtras : null;
- }
-
- private void sendMyPackageSuspendedOrUnsuspended(String[] affectedPackages, boolean suspended,
- int userId) {
- final String action = suspended
- ? Intent.ACTION_MY_PACKAGE_SUSPENDED
- : Intent.ACTION_MY_PACKAGE_UNSUSPENDED;
- mHandler.post(() -> {
- final IActivityManager am = ActivityManager.getService();
- if (am == null) {
- Slog.wtf(TAG, "IActivityManager null. Cannot send MY_PACKAGE_ "
- + (suspended ? "" : "UN") + "SUSPENDED broadcasts");
- return;
- }
- final int[] targetUserIds = new int[] {userId};
- for (String packageName : affectedPackages) {
- final Bundle appExtras = suspended
- ? getSuspendedPackageAppExtrasInternal(packageName, userId)
- : null;
- final Bundle intentExtras;
- if (appExtras != null) {
- intentExtras = new Bundle(1);
- intentExtras.putBundle(Intent.EXTRA_SUSPENDED_PACKAGE_EXTRAS, appExtras);
- } else {
- intentExtras = null;
- }
- mHandler.post(() -> mBroadcastHelper.doSendBroadcast(action, null, intentExtras,
- Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, packageName, null,
- targetUserIds, false, null, null));
- }
- });
+ return mSuspendPackageHelper.getSuspendedPackageAppExtras(
+ packageName, userId, callingUid);
}
@Override
@@ -4618,50 +4454,14 @@ public class PackageManagerService extends IPackageManager.Stub
synchronized (mLock) {
allPackages = mPackages.keySet().toArray(new String[mPackages.size()]);
}
- removeSuspensionsBySuspendingPackage(allPackages, suspendingPackage::equals, userId);
+ mSuspendPackageHelper.removeSuspensionsBySuspendingPackage(
+ allPackages, suspendingPackage::equals, userId);
}
private boolean isSuspendingAnyPackages(String suspendingPackage, int userId) {
return mComputer.isSuspendingAnyPackages(suspendingPackage, userId);
}
- /**
- * Removes any suspensions on given packages that were added by packages that pass the given
- * predicate.
- *
- * <p> Caller must flush package restrictions if it cares about immediate data consistency.
- *
- * @param packagesToChange The packages on which the suspension are to be removed.
- * @param suspendingPackagePredicate A predicate identifying the suspending packages whose
- * suspensions will be removed.
- * @param userId The user for which the changes are taking place.
- */
- private void removeSuspensionsBySuspendingPackage(String[] packagesToChange,
- Predicate<String> suspendingPackagePredicate, int userId) {
- final List<String> unsuspendedPackages = new ArrayList<>();
- final IntArray unsuspendedUids = new IntArray();
- synchronized (mLock) {
- for (String packageName : packagesToChange) {
- final PackageSetting ps = mSettings.getPackageLPr(packageName);
- if (ps != null && ps.getUserStateOrDefault(userId).isSuspended()) {
- ps.removeSuspension(suspendingPackagePredicate, userId);
- if (!ps.getUserStateOrDefault(userId).isSuspended()) {
- unsuspendedPackages.add(ps.getPackageName());
- unsuspendedUids.add(UserHandle.getUid(userId, ps.getAppId()));
- }
- }
- }
- scheduleWritePackageRestrictionsLocked(userId);
- }
- if (!unsuspendedPackages.isEmpty()) {
- final String[] packageArray = unsuspendedPackages.toArray(
- new String[unsuspendedPackages.size()]);
- sendMyPackageSuspendedOrUnsuspended(packageArray, false, userId);
- sendPackagesSuspendedForUser(Intent.ACTION_PACKAGES_UNSUSPENDED,
- packageArray, unsuspendedUids.toArray(), userId);
- }
- }
-
void removeAllDistractingPackageRestrictions(int userId) {
final String[] allPackages = mComputer.getAllAvailablePackageNames();
removeDistractingPackageRestrictions(allPackages, userId);
@@ -4698,24 +4498,6 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- private boolean isCallerDeviceOrProfileOwner(int userId) {
- final int callingUid = Binder.getCallingUid();
- if (callingUid == Process.SYSTEM_UID) {
- return true;
- }
- final String ownerPackage = mProtectedPackages.getDeviceOwnerOrProfileOwnerPackage(userId);
- if (ownerPackage != null) {
- return callingUid == getPackageUidInternal(ownerPackage, 0, userId, callingUid);
- }
- return false;
- }
-
- private boolean isSuspendAllowedForUser(int userId) {
- return isCallerDeviceOrProfileOwner(userId)
- || (!mUserManager.hasUserRestriction(UserManager.DISALLOW_APPS_CONTROL, userId)
- && !mUserManager.hasUserRestriction(UserManager.DISALLOW_UNINSTALL_APPS, userId));
- }
-
@Override
public String[] getUnsuspendablePackagesForUser(String[] packageNames, int userId) {
Objects.requireNonNull(packageNames, "packageNames cannot be null");
@@ -4726,125 +4508,8 @@ public class PackageManagerService extends IPackageManager.Stub
throw new SecurityException("Calling uid " + callingUid
+ " cannot query getUnsuspendablePackagesForUser for user " + userId);
}
- if (!isSuspendAllowedForUser(userId)) {
- Slog.w(TAG, "Cannot suspend due to restrictions on user " + userId);
- return packageNames;
- }
- final ArraySet<String> unactionablePackages = new ArraySet<>();
- final boolean[] canSuspend = canSuspendPackageForUserInternal(packageNames, userId);
- for (int i = 0; i < packageNames.length; i++) {
- if (!canSuspend[i]) {
- unactionablePackages.add(packageNames[i]);
- continue;
- }
- synchronized (mLock) {
- final PackageSetting ps = mSettings.getPackageLPr(packageNames[i]);
- if (ps == null || shouldFilterApplication(ps, callingUid, userId)) {
- Slog.w(TAG, "Could not find package setting for package: " + packageNames[i]);
- unactionablePackages.add(packageNames[i]);
- }
- }
- }
- return unactionablePackages.toArray(new String[unactionablePackages.size()]);
- }
-
- /**
- * Returns an array of booleans, such that the ith boolean denotes whether the ith package can
- * be suspended or not.
- *
- * @param packageNames The package names to check suspendability for.
- * @param userId The user to check in
- * @return An array containing results of the checks
- */
- @NonNull
- private boolean[] canSuspendPackageForUserInternal(@NonNull String[] packageNames, int userId) {
- final boolean[] canSuspend = new boolean[packageNames.length];
- final boolean isCallerOwner = isCallerDeviceOrProfileOwner(userId);
- final long callingId = Binder.clearCallingIdentity();
- try {
- final String activeLauncherPackageName = getActiveLauncherPackageName(userId);
- final String dialerPackageName = mDefaultAppProvider.getDefaultDialer(userId);
- for (int i = 0; i < packageNames.length; i++) {
- canSuspend[i] = false;
- final String packageName = packageNames[i];
-
- if (isPackageDeviceAdmin(packageName, userId)) {
- Slog.w(TAG, "Cannot suspend package \"" + packageName
- + "\": has an active device admin");
- continue;
- }
- if (packageName.equals(activeLauncherPackageName)) {
- Slog.w(TAG, "Cannot suspend package \"" + packageName
- + "\": contains the active launcher");
- continue;
- }
- if (packageName.equals(mRequiredInstallerPackage)) {
- Slog.w(TAG, "Cannot suspend package \"" + packageName
- + "\": required for package installation");
- continue;
- }
- if (packageName.equals(mRequiredUninstallerPackage)) {
- Slog.w(TAG, "Cannot suspend package \"" + packageName
- + "\": required for package uninstallation");
- continue;
- }
- if (packageName.equals(mRequiredVerifierPackage)) {
- Slog.w(TAG, "Cannot suspend package \"" + packageName
- + "\": required for package verification");
- continue;
- }
- if (packageName.equals(dialerPackageName)) {
- Slog.w(TAG, "Cannot suspend package \"" + packageName
- + "\": is the default dialer");
- continue;
- }
- if (packageName.equals(mRequiredPermissionControllerPackage)) {
- Slog.w(TAG, "Cannot suspend package \"" + packageName
- + "\": required for permissions management");
- continue;
- }
- synchronized (mLock) {
- if (mProtectedPackages.isPackageStateProtected(userId, packageName)) {
- Slog.w(TAG, "Cannot suspend package \"" + packageName
- + "\": protected package");
- continue;
- }
- if (!isCallerOwner && mSettings.getBlockUninstallLPr(userId, packageName)) {
- Slog.w(TAG, "Cannot suspend package \"" + packageName
- + "\": blocked by admin");
- continue;
- }
-
- AndroidPackage pkg = mPackages.get(packageName);
- if (pkg != null) {
- // Cannot suspend SDK libs as they are controlled by SDK manager.
- if (pkg.isSdkLibrary()) {
- Slog.w(TAG, "Cannot suspend package: " + packageName
- + " providing SDK library: "
- + pkg.getSdkLibName());
- continue;
- }
- // Cannot suspend static shared libs as they are considered
- // a part of the using app (emulating static linking). Also
- // static libs are installed always on internal storage.
- if (pkg.isStaticSharedLibrary()) {
- Slog.w(TAG, "Cannot suspend package: " + packageName
- + " providing static shared library: "
- + pkg.getStaticSharedLibName());
- continue;
- }
- }
- }
- if (PLATFORM_PACKAGE_NAME.equals(packageName)) {
- Slog.w(TAG, "Cannot suspend the platform package: " + packageName);
- continue;
- }
- canSuspend[i] = true;
- }
- } finally {
- Binder.restoreCallingIdentity(callingId);
- }
- return canSuspend;
+ return mSuspendPackageHelper.getUnsuspendablePackagesForUser(
+ packageNames, userId, callingUid);
}
@Override
@@ -5445,7 +5110,7 @@ public class PackageManagerService extends IPackageManager.Stub
FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL);
final int appId = UserHandle.getAppId(pkg.getUid());
- removeKeystoreDataIfNeeded(mInjector.getUserManagerInternal(), userId, appId);
+ mAppDataHelper.clearKeystoreData(userId, appId);
UserManagerInternal umInternal = mInjector.getUserManagerInternal();
StorageManagerInternal smInternal = mInjector.getLocalService(StorageManagerInternal.class);
@@ -5518,30 +5183,6 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- /**
- * Remove entries from the keystore daemon. Will only remove it if the
- * {@code appId} is valid.
- */
- static void removeKeystoreDataIfNeeded(UserManagerInternal um, @UserIdInt int userId,
- @AppIdInt int appId) {
- if (appId < 0) {
- return;
- }
-
- final KeyStore keyStore = KeyStore.getInstance();
- if (keyStore != null) {
- if (userId == UserHandle.USER_ALL) {
- for (final int individual : um.getUserIds()) {
- keyStore.clearUid(UserHandle.getUid(individual, appId));
- }
- } else {
- keyStore.clearUid(UserHandle.getUid(userId, appId));
- }
- } else {
- Slog.w(TAG, "Could not contact keystore to clear entries for app id " + appId);
- }
- }
-
@Override
public void deleteApplicationCacheFiles(final String packageName,
final IPackageDataObserver observer) {
@@ -5980,6 +5621,11 @@ public class PackageManagerService extends IPackageManager.Stub
return mPmInternal.getSetupWizardPackageName();
}
+ public @Nullable String getAmbientContextDetectionPackageName() {
+ return ensureSystemPackageName(getPackageFromComponentString(
+ R.string.config_defaultAmbientContextDetectionService));
+ }
+
public String getIncidentReportApproverPackageName() {
return ensureSystemPackageName(mContext.getString(
R.string.config_incidentReportApproverPackage));
@@ -7420,41 +7066,27 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public Bundle getSuspendedPackageLauncherExtras(String packageName, int userId) {
- final PackageStateInternal packageState = getPackageStateInternal(packageName);
- if (packageState == null) {
- return null;
- }
- Bundle allExtras = new Bundle();
- PackageUserStateInternal userState = packageState.getUserStateOrDefault(userId);
- if (userState.isSuspended()) {
- for (int i = 0; i < userState.getSuspendParams().size(); i++) {
- final SuspendParams params = userState.getSuspendParams().valueAt(i);
- if (params != null && params.getLauncherExtras() != null) {
- allExtras.putAll(params.getLauncherExtras());
- }
- }
- }
- return (allExtras.size() > 0) ? allExtras : null;
+ return mSuspendPackageHelper.getSuspendedPackageLauncherExtras(
+ packageName, userId, Binder.getCallingUid());
}
@Override
public boolean isPackageSuspended(String packageName, int userId) {
- final PackageStateInternal packageState = getPackageStateInternal(packageName);
- return packageState != null && packageState.getUserStateOrDefault(userId)
- .isSuspended();
+ return mSuspendPackageHelper.isPackageSuspended(
+ packageName, userId, Binder.getCallingUid());
}
@Override
public void removeAllNonSystemPackageSuspensions(int userId) {
final String[] allPackages = mComputer.getAllAvailablePackageNames();
- PackageManagerService.this.removeSuspensionsBySuspendingPackage(allPackages,
+ mSuspendPackageHelper.removeSuspensionsBySuspendingPackage(allPackages,
(suspendingPackage) -> !PLATFORM_PACKAGE_NAME.equals(suspendingPackage),
userId);
}
@Override
public void removeNonSystemPackageSuspensions(String packageName, int userId) {
- PackageManagerService.this.removeSuspensionsBySuspendingPackage(
+ mSuspendPackageHelper.removeSuspensionsBySuspendingPackage(
new String[]{packageName},
(suspendingPackage) -> !PLATFORM_PACKAGE_NAME.equals(suspendingPackage),
userId);
@@ -7480,46 +7112,15 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public String getSuspendingPackage(String suspendedPackage, int userId) {
- final PackageStateInternal packageState = getPackageStateInternal(suspendedPackage);
- if (packageState == null) {
- return null;
- }
-
- final PackageUserStateInternal userState = packageState.getUserStateOrDefault(userId);
- if (!userState.isSuspended()) {
- return null;
- }
-
- String suspendingPackage = null;
- for (int i = 0; i < userState.getSuspendParams().size(); i++) {
- suspendingPackage = userState.getSuspendParams().keyAt(i);
- if (PLATFORM_PACKAGE_NAME.equals(suspendingPackage)) {
- return suspendingPackage;
- }
- }
- return suspendingPackage;
+ return mSuspendPackageHelper.getSuspendingPackage(
+ suspendedPackage, userId, Binder.getCallingUid());
}
@Override
public SuspendDialogInfo getSuspendedDialogInfo(String suspendedPackage,
String suspendingPackage, int userId) {
- final PackageStateInternal packageState = getPackageStateInternal(suspendedPackage);
- if (packageState == null) {
- return null;
- }
-
- final PackageUserStateInternal userState = packageState.getUserStateOrDefault(userId);
- if (!userState.isSuspended()) {
- return null;
- }
-
- final ArrayMap<String, SuspendParams> suspendParamsMap = userState.getSuspendParams();
- if (suspendParamsMap == null) {
- return null;
- }
-
- final SuspendParams suspendParams = suspendParamsMap.get(suspendingPackage);
- return (suspendParams != null) ? suspendParams.getDialogInfo() : null;
+ return mSuspendPackageHelper.getSuspendedDialogInfo(
+ suspendedPackage, suspendingPackage, userId, Binder.getCallingUid());
}
@Override
@@ -9083,6 +8684,8 @@ public class PackageManagerService extends IPackageManager.Stub
return new String[] { mDefaultAppProvider.getDefaultBrowser(userId) };
case PackageManagerInternal.PACKAGE_INSTALLER:
return mComputer.filterOnlySystemPackages(mRequiredInstallerPackage);
+ case PackageManagerInternal.PACKAGE_UNINSTALLER:
+ return mComputer.filterOnlySystemPackages(mRequiredUninstallerPackage);
case PackageManagerInternal.PACKAGE_SETUP_WIZARD:
return mComputer.filterOnlySystemPackages(mSetupWizardPackage);
case PackageManagerInternal.PACKAGE_SYSTEM:
@@ -9098,6 +8701,8 @@ public class PackageManagerService extends IPackageManager.Stub
return mComputer.filterOnlySystemPackages(mConfiguratorPackage);
case PackageManagerInternal.PACKAGE_INCIDENT_REPORT_APPROVER:
return mComputer.filterOnlySystemPackages(mIncidentReportApproverPackage);
+ case PackageManagerInternal.PACKAGE_AMBIENT_CONTEXT_DETECTION:
+ return mComputer.filterOnlySystemPackages(mAmbientContextDetectionPackage);
case PackageManagerInternal.PACKAGE_APP_PREDICTOR:
return mComputer.filterOnlySystemPackages(mAppPredictionServicePackage);
case PackageManagerInternal.PACKAGE_COMPANION:
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
index a1acc388146e..db606863248a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
@@ -91,6 +91,7 @@ public final class PackageManagerServiceTestParams {
public ViewCompiler viewCompiler;
public @Nullable String retailDemoPackage;
public @Nullable String recentsPackage;
+ public @Nullable String ambientContextDetectionPackage;
public ComponentName resolveComponentName;
public ArrayMap<String, AndroidPackage> packages;
public boolean enableFreeCacheV2;
@@ -111,4 +112,5 @@ public final class PackageManagerServiceTestParams {
public PreferredActivityHelper preferredActivityHelper;
public ResolveIntentHelper resolveIntentHelper;
public DexOptHelper dexOptHelper;
+ public SuspendPackageHelper suspendPackageHelper;
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index d8f0cc340884..e03cf0a10537 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -51,7 +51,6 @@ import android.content.pm.Signature;
import android.content.pm.SigningDetails;
import android.content.pm.parsing.ApkLiteParseUtils;
import android.content.pm.parsing.PackageLite;
-import com.android.server.pm.pkg.component.ParsedMainComponent;
import android.content.pm.parsing.result.ParseResult;
import android.content.pm.parsing.result.ParseTypeImpl;
import android.os.Binder;
@@ -92,6 +91,7 @@ import com.android.server.compat.PlatformCompat;
import com.android.server.pm.dex.PackageDexUsage;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.pkg.component.ParsedMainComponent;
import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
import dalvik.system.VMRuntime;
@@ -560,8 +560,8 @@ public class PackageManagerServiceUtils {
if (!match) {
throw new PackageManagerException(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
- "Package " + packageName +
- " signatures do not match previously installed version; ignoring!");
+ "Existing package " + packageName
+ + " signatures do not match newer version; ignoring!");
}
}
// Check for shared user signatures
diff --git a/services/core/java/com/android/server/pm/RemovePackageHelper.java b/services/core/java/com/android/server/pm/RemovePackageHelper.java
index b0e03403b653..7e898cbe86b0 100644
--- a/services/core/java/com/android/server/pm/RemovePackageHelper.java
+++ b/services/core/java/com/android/server/pm/RemovePackageHelper.java
@@ -29,7 +29,6 @@ import static com.android.server.pm.PackageManagerService.TAG;
import android.annotation.NonNull;
import android.content.pm.PackageManager;
-import com.android.server.pm.pkg.component.ParsedInstrumentation;
import android.os.UserHandle;
import android.os.incremental.IncrementalManager;
import android.util.Log;
@@ -43,6 +42,7 @@ import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.parsing.pkg.PackageImpl;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.pkg.component.ParsedInstrumentation;
import java.io.File;
import java.util.Collections;
@@ -330,8 +330,7 @@ final class RemovePackageHelper {
if (removedAppId != -1) {
// A user ID was deleted here. Go through all users and remove it
// from KeyStore.
- mPm.removeKeystoreDataIfNeeded(
- mUserManagerInternal, UserHandle.USER_ALL, removedAppId);
+ mAppDataHelper.clearKeystoreData(UserHandle.USER_ALL, removedAppId);
}
}
}
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index bf7ef1b24776..15e64dffe892 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -146,8 +146,10 @@ class ShortcutPackage extends ShortcutPackageItem {
private static final String ATTR_PERSON_IS_IMPORTANT = "is-important";
private static final String NAME_CATEGORIES = "categories";
+ private static final String NAME_CAPABILITY = "capability";
private static final String TAG_STRING_ARRAY_XMLUTILS = "string-array";
+ private static final String TAG_MAP_XMLUTILS = "map";
private static final String ATTR_NAME_XMLUTILS = "name";
private static final String KEY_DYNAMIC = "dynamic";
@@ -1829,6 +1831,12 @@ class ShortcutPackage extends ShortcutPackageItem {
}
ShortcutService.writeTagExtra(out, TAG_EXTRAS, si.getExtras());
+
+ final Map<String, Map<String, List<String>>> capabilityBindings =
+ si.getCapabilityBindings();
+ if (capabilityBindings != null && !capabilityBindings.isEmpty()) {
+ XmlUtils.writeMapXml(si.getCapabilityBindings(), NAME_CAPABILITY, out);
+ }
}
out.endTag(null, TAG_SHORTCUT);
@@ -1961,6 +1969,7 @@ class ShortcutPackage extends ShortcutPackageItem {
int backupVersionCode;
ArraySet<String> categories = null;
ArrayList<Person> persons = new ArrayList<>();
+ Map<String, Map<String, List<String>>> capabilityBindings = null;
id = ShortcutService.parseStringAttribute(parser, ATTR_ID);
activityComponent = ShortcutService.parseComponentNameAttribute(parser,
@@ -2029,6 +2038,13 @@ class ShortcutPackage extends ShortcutPackageItem {
}
}
continue;
+ case TAG_MAP_XMLUTILS:
+ if (NAME_CAPABILITY.equals(ShortcutService.parseStringAttribute(parser,
+ ATTR_NAME_XMLUTILS))) {
+ capabilityBindings = (Map<String, Map<String, List<String>>>)
+ XmlUtils.readValueXml(parser, new String[1]);
+ }
+ continue;
}
throw ShortcutService.throwForInvalidTag(depth, tag);
}
@@ -2064,7 +2080,7 @@ class ShortcutPackage extends ShortcutPackageItem {
rank, extras, lastChangedTimestamp, flags,
iconResId, iconResName, bitmapPath, iconUri,
disabledReason, persons.toArray(new Person[persons.size()]), locusId,
- splashScreenThemeResName);
+ splashScreenThemeResName, capabilityBindings);
}
private static Intent parseIntent(TypedXmlPullParser parser)
diff --git a/services/core/java/com/android/server/pm/ShortcutParser.java b/services/core/java/com/android/server/pm/ShortcutParser.java
index b86c50b23687..63f1f2d518f7 100644
--- a/services/core/java/com/android/server/pm/ShortcutParser.java
+++ b/services/core/java/com/android/server/pm/ShortcutParser.java
@@ -459,7 +459,8 @@ public class ShortcutParser {
disabledReason,
null /* persons */,
null /* locusId */,
- splashScreenThemeResName);
+ splashScreenThemeResName,
+ null /* capabilityBindings */);
}
private static String parseCategory(ShortcutService service, AttributeSet attrs) {
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 0a2735cdbf76..057f8def1798 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -3474,8 +3474,8 @@ public class ShortcutService extends IShortcutService.Stub {
@Nullable
private ParcelFileDescriptor getShortcutIconParcelFileDescriptor(
- @NonNull final ShortcutInfo shortcutInfo) {
- if (!shortcutInfo.hasIconFile()) {
+ @Nullable final ShortcutInfo shortcutInfo) {
+ if (shortcutInfo == null || !shortcutInfo.hasIconFile()) {
return null;
}
final String path = mShortcutBitmapSaver.getBitmapPathMayWaitLocked(shortcutInfo);
diff --git a/services/core/java/com/android/server/pm/SuspendPackageHelper.java b/services/core/java/com/android/server/pm/SuspendPackageHelper.java
new file mode 100644
index 000000000000..f466ca72f681
--- /dev/null
+++ b/services/core/java/com/android/server/pm/SuspendPackageHelper.java
@@ -0,0 +1,611 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import static android.content.pm.PackageManagerInternal.PACKAGE_INSTALLER;
+import static android.content.pm.PackageManagerInternal.PACKAGE_PERMISSION_CONTROLLER;
+import static android.content.pm.PackageManagerInternal.PACKAGE_UNINSTALLER;
+import static android.content.pm.PackageManagerInternal.PACKAGE_VERIFIER;
+import static android.os.Process.SYSTEM_UID;
+
+import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
+import static com.android.server.pm.PackageManagerService.TAG;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.IActivityManager;
+import android.content.Intent;
+import android.content.pm.PackageManagerInternal.KnownPackage;
+import android.content.pm.SuspendDialogInfo;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.PersistableBundle;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.IntArray;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.pkg.PackageUserStateInternal;
+import com.android.server.pm.pkg.SuspendParams;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.Predicate;
+
+public final class SuspendPackageHelper {
+ // TODO(b/198166813): remove PMS dependency
+ private final PackageManagerService mPm;
+ private final PackageManagerServiceInjector mInjector;
+
+ private final BroadcastHelper mBroadcastHelper;
+ private final ProtectedPackages mProtectedPackages;
+
+ /**
+ * Constructor for {@link PackageManagerService}.
+ */
+ SuspendPackageHelper(PackageManagerService pm, PackageManagerServiceInjector injector,
+ BroadcastHelper broadcastHelper, ProtectedPackages protectedPackages) {
+ mPm = pm;
+ mInjector = injector;
+ mBroadcastHelper = broadcastHelper;
+ mProtectedPackages = protectedPackages;
+ }
+
+ /**
+ * Updates the package to the suspended or unsuspended state.
+ *
+ * @param packageNames The names of the packages to set the suspended status.
+ * @param suspended {@code true} to suspend packages, or {@code false} to unsuspend packages.
+ * @param appExtras An optional {@link PersistableBundle} that the suspending app can provide
+ * which will be shared with the apps being suspended. Ignored if
+ * {@code suspended} is false.
+ * @param launcherExtras An optional {@link PersistableBundle} that the suspending app can
+ * provide which will be shared with the launcher. Ignored if
+ * {@code suspended} is false.
+ * @param dialogInfo An optional {@link SuspendDialogInfo} object describing the dialog that
+ * should be shown to the user when they try to launch a suspended app.
+ * Ignored if {@code suspended} is false.
+ * @param callingPackage The caller's package name.
+ * @param userId The user where packages reside.
+ * @param callingUid The caller's uid.
+ * @return The names of failed packages.
+ */
+ @Nullable
+ String[] setPackagesSuspended(@Nullable String[] packageNames, boolean suspended,
+ @Nullable PersistableBundle appExtras, @Nullable PersistableBundle launcherExtras,
+ @Nullable SuspendDialogInfo dialogInfo, @NonNull String callingPackage,
+ int userId, int callingUid) {
+ if (ArrayUtils.isEmpty(packageNames)) {
+ return packageNames;
+ }
+ if (suspended && !isSuspendAllowedForUser(userId, callingUid)) {
+ Slog.w(TAG, "Cannot suspend due to restrictions on user " + userId);
+ return packageNames;
+ }
+
+ final List<String> changedPackagesList = new ArrayList<>(packageNames.length);
+ final IntArray changedUids = new IntArray(packageNames.length);
+ final List<String> modifiedPackagesList = new ArrayList<>(packageNames.length);
+ final IntArray modifiedUids = new IntArray(packageNames.length);
+ final List<String> unactionedPackages = new ArrayList<>(packageNames.length);
+ final boolean[] canSuspend =
+ suspended ? canSuspendPackageForUser(packageNames, userId, callingUid) : null;
+
+ for (int i = 0; i < packageNames.length; i++) {
+ final String packageName = packageNames[i];
+ if (callingPackage.equals(packageName)) {
+ Slog.w(TAG, "Calling package: " + callingPackage + " trying to "
+ + (suspended ? "" : "un") + "suspend itself. Ignoring");
+ unactionedPackages.add(packageName);
+ continue;
+ }
+ final PackageSetting pkgSetting;
+ synchronized (mPm.mLock) {
+ pkgSetting = mPm.mSettings.getPackageLPr(packageName);
+ if (pkgSetting == null
+ || mPm.shouldFilterApplication(pkgSetting, callingUid, userId)) {
+ Slog.w(TAG, "Could not find package setting for package: " + packageName
+ + ". Skipping suspending/un-suspending.");
+ unactionedPackages.add(packageName);
+ continue;
+ }
+ }
+ if (canSuspend != null && !canSuspend[i]) {
+ unactionedPackages.add(packageName);
+ continue;
+ }
+ final boolean packageUnsuspended;
+ final boolean packageModified;
+ synchronized (mPm.mLock) {
+ if (suspended) {
+ packageModified = pkgSetting.addOrUpdateSuspension(callingPackage,
+ dialogInfo, appExtras, launcherExtras, userId);
+ } else {
+ packageModified = pkgSetting.removeSuspension(callingPackage, userId);
+ }
+ packageUnsuspended = !suspended && !pkgSetting.getSuspended(userId);
+ }
+ if (suspended || packageUnsuspended) {
+ changedPackagesList.add(packageName);
+ changedUids.add(UserHandle.getUid(userId, pkgSetting.getAppId()));
+ }
+ if (packageModified) {
+ modifiedPackagesList.add(packageName);
+ modifiedUids.add(UserHandle.getUid(userId, pkgSetting.getAppId()));
+ }
+ }
+
+ if (!changedPackagesList.isEmpty()) {
+ final String[] changedPackages = changedPackagesList.toArray(new String[0]);
+ sendPackagesSuspendedForUser(
+ suspended ? Intent.ACTION_PACKAGES_SUSPENDED
+ : Intent.ACTION_PACKAGES_UNSUSPENDED,
+ changedPackages, changedUids.toArray(), userId);
+ sendMyPackageSuspendedOrUnsuspended(changedPackages, suspended, userId);
+ synchronized (mPm.mLock) {
+ mPm.scheduleWritePackageRestrictionsLocked(userId);
+ }
+ }
+ // Send the suspension changed broadcast to ensure suspension state is not stale.
+ if (!modifiedPackagesList.isEmpty()) {
+ sendPackagesSuspendedForUser(Intent.ACTION_PACKAGES_SUSPENSION_CHANGED,
+ modifiedPackagesList.toArray(new String[0]), modifiedUids.toArray(), userId);
+ }
+ return unactionedPackages.toArray(new String[0]);
+ }
+
+ /**
+ * Returns the names in the {@code packageNames} which can not be suspended by the caller.
+ *
+ * @param packageNames The names of packages to check.
+ * @param userId The user where packages reside.
+ * @param callingUid The caller's uid.
+ * @return The names of packages which are Unsuspendable.
+ */
+ @NonNull
+ String[] getUnsuspendablePackagesForUser(@NonNull String[] packageNames, int userId,
+ int callingUid) {
+ if (!isSuspendAllowedForUser(userId, callingUid)) {
+ Slog.w(TAG, "Cannot suspend due to restrictions on user " + userId);
+ return packageNames;
+ }
+ final ArraySet<String> unactionablePackages = new ArraySet<>();
+ final boolean[] canSuspend = canSuspendPackageForUser(packageNames, userId, callingUid);
+ for (int i = 0; i < packageNames.length; i++) {
+ if (!canSuspend[i]) {
+ unactionablePackages.add(packageNames[i]);
+ continue;
+ }
+ synchronized (mPm.mLock) {
+ final PackageSetting ps = mPm.mSettings.getPackageLPr(packageNames[i]);
+ if (ps == null || mPm.shouldFilterApplication(ps, callingUid, userId)) {
+ Slog.w(TAG, "Could not find package setting for package: " + packageNames[i]);
+ unactionablePackages.add(packageNames[i]);
+ }
+ }
+ }
+ return unactionablePackages.toArray(new String[unactionablePackages.size()]);
+ }
+
+ /**
+ * Returns the app extras of the given suspended package.
+ *
+ * @param packageName The suspended package name.
+ * @param userId The user where the package resides.
+ * @param callingUid The caller's uid.
+ * @return The app extras of the suspended package.
+ */
+ @Nullable
+ Bundle getSuspendedPackageAppExtras(@NonNull String packageName, int userId, int callingUid) {
+ final PackageStateInternal ps = mPm.getPackageStateInternal(packageName, callingUid);
+ if (ps == null) {
+ return null;
+ }
+ final PackageUserStateInternal pus = ps.getUserStateOrDefault(userId);
+ final Bundle allExtras = new Bundle();
+ if (pus.isSuspended()) {
+ for (int i = 0; i < pus.getSuspendParams().size(); i++) {
+ final SuspendParams params = pus.getSuspendParams().valueAt(i);
+ if (params != null && params.getAppExtras() != null) {
+ allExtras.putAll(params.getAppExtras());
+ }
+ }
+ }
+ return (allExtras.size() > 0) ? allExtras : null;
+ }
+
+ /**
+ * Removes any suspensions on given packages that were added by packages that pass the given
+ * predicate.
+ *
+ * <p> Caller must flush package restrictions if it cares about immediate data consistency.
+ *
+ * @param packagesToChange The packages on which the suspension are to be removed.
+ * @param suspendingPackagePredicate A predicate identifying the suspending packages whose
+ * suspensions will be removed.
+ * @param userId The user for which the changes are taking place.
+ */
+ void removeSuspensionsBySuspendingPackage(@NonNull String[] packagesToChange,
+ @NonNull Predicate<String> suspendingPackagePredicate, int userId) {
+ final List<String> unsuspendedPackages = new ArrayList<>();
+ final IntArray unsuspendedUids = new IntArray();
+ synchronized (mPm.mLock) {
+ for (String packageName : packagesToChange) {
+ final PackageSetting ps = mPm.mSettings.getPackageLPr(packageName);
+ if (ps != null && ps.getUserStateOrDefault(userId).isSuspended()) {
+ ps.removeSuspension(suspendingPackagePredicate, userId);
+ if (!ps.getUserStateOrDefault(userId).isSuspended()) {
+ unsuspendedPackages.add(ps.getPackageName());
+ unsuspendedUids.add(UserHandle.getUid(userId, ps.getAppId()));
+ }
+ }
+ }
+ mPm.scheduleWritePackageRestrictionsLocked(userId);
+ }
+ if (!unsuspendedPackages.isEmpty()) {
+ final String[] packageArray = unsuspendedPackages.toArray(
+ new String[unsuspendedPackages.size()]);
+ sendMyPackageSuspendedOrUnsuspended(packageArray, false, userId);
+ sendPackagesSuspendedForUser(Intent.ACTION_PACKAGES_UNSUSPENDED,
+ packageArray, unsuspendedUids.toArray(), userId);
+ }
+ }
+
+ /**
+ * Returns the launcher extras for the given suspended package.
+ *
+ * @param packageName The name of the suspended package.
+ * @param userId The user where the package resides.
+ * @param callingUid The caller's uid.
+ * @return The launcher extras.
+ */
+ @Nullable
+ Bundle getSuspendedPackageLauncherExtras(@NonNull String packageName, int userId,
+ int callingUid) {
+ final PackageStateInternal packageState = mPm.getPackageStateInternal(
+ packageName, callingUid);
+ if (packageState == null) {
+ return null;
+ }
+ Bundle allExtras = new Bundle();
+ PackageUserStateInternal userState = packageState.getUserStateOrDefault(userId);
+ if (userState.isSuspended()) {
+ for (int i = 0; i < userState.getSuspendParams().size(); i++) {
+ final SuspendParams params = userState.getSuspendParams().valueAt(i);
+ if (params != null && params.getLauncherExtras() != null) {
+ allExtras.putAll(params.getLauncherExtras());
+ }
+ }
+ }
+ return (allExtras.size() > 0) ? allExtras : null;
+ }
+
+ /**
+ * Return {@code true}, if the given package is suspended.
+ *
+ * @param packageName The name of package to check.
+ * @param userId The user where the package resides.
+ * @param callingUid The caller's uid.
+ * @return {@code true}, if the given package is suspended.
+ */
+ boolean isPackageSuspended(@NonNull String packageName, int userId, int callingUid) {
+ final PackageStateInternal packageState = mPm.getPackageStateInternal(
+ packageName, callingUid);
+ return packageState != null && packageState.getUserStateOrDefault(userId)
+ .isSuspended();
+ }
+
+ /**
+ * Given a suspended package, returns the name of package which invokes suspending to it.
+ *
+ * @param suspendedPackage The suspended package to check.
+ * @param userId The user where the package resides.
+ * @param callingUid The caller's uid.
+ * @return The name of suspending package.
+ */
+ @Nullable
+ String getSuspendingPackage(@NonNull String suspendedPackage, int userId, int callingUid) {
+ final PackageStateInternal packageState = mPm.getPackageStateInternal(
+ suspendedPackage, callingUid);
+ if (packageState == null) {
+ return null;
+ }
+
+ final PackageUserStateInternal userState = packageState.getUserStateOrDefault(userId);
+ if (!userState.isSuspended()) {
+ return null;
+ }
+
+ String suspendingPackage = null;
+ for (int i = 0; i < userState.getSuspendParams().size(); i++) {
+ suspendingPackage = userState.getSuspendParams().keyAt(i);
+ if (PLATFORM_PACKAGE_NAME.equals(suspendingPackage)) {
+ return suspendingPackage;
+ }
+ }
+ return suspendingPackage;
+ }
+
+ /**
+ * Returns the dialog info of the given suspended package.
+ *
+ * @param suspendedPackage The name of the suspended package.
+ * @param suspendingPackage The name of the suspending package.
+ * @param userId The user where the package resides.
+ * @param callingUid The caller's uid.
+ * @return The dialog info.
+ */
+ @Nullable
+ SuspendDialogInfo getSuspendedDialogInfo(@NonNull String suspendedPackage,
+ @NonNull String suspendingPackage, int userId, int callingUid) {
+ final PackageStateInternal packageState = mPm.getPackageStateInternal(
+ suspendedPackage, callingUid);
+ if (packageState == null) {
+ return null;
+ }
+
+ final PackageUserStateInternal userState = packageState.getUserStateOrDefault(userId);
+ if (!userState.isSuspended()) {
+ return null;
+ }
+
+ final ArrayMap<String, SuspendParams> suspendParamsMap = userState.getSuspendParams();
+ if (suspendParamsMap == null) {
+ return null;
+ }
+
+ final SuspendParams suspendParams = suspendParamsMap.get(suspendingPackage);
+ return (suspendParams != null) ? suspendParams.getDialogInfo() : null;
+ }
+
+ /**
+ * Return {@code true} if the user is allowed to suspend packages by the caller.
+ *
+ * @param userId The user id to check.
+ * @param callingUid The caller's uid.
+ * @return {@code true} if the user is allowed to suspend packages by the caller.
+ */
+ boolean isSuspendAllowedForUser(int userId, int callingUid) {
+ final UserManagerService userManager = mInjector.getUserManagerService();
+ return isCallerDeviceOrProfileOwner(userId, callingUid)
+ || (!userManager.hasUserRestriction(UserManager.DISALLOW_APPS_CONTROL, userId)
+ && !userManager.hasUserRestriction(UserManager.DISALLOW_UNINSTALL_APPS, userId));
+ }
+
+ /**
+ * Returns an array of booleans, such that the ith boolean denotes whether the ith package can
+ * be suspended or not.
+ *
+ * @param packageNames The package names to check suspendability for.
+ * @param userId The user to check in
+ * @param callingUid The caller's uid.
+ * @return An array containing results of the checks
+ */
+ @NonNull
+ boolean[] canSuspendPackageForUser(@NonNull String[] packageNames, int userId, int callingUid) {
+ final boolean[] canSuspend = new boolean[packageNames.length];
+ final boolean isCallerOwner = isCallerDeviceOrProfileOwner(userId, callingUid);
+ final long token = Binder.clearCallingIdentity();
+ try {
+ final DefaultAppProvider defaultAppProvider = mInjector.getDefaultAppProvider();
+ final String activeLauncherPackageName = defaultAppProvider.getDefaultHome(userId);
+ final String dialerPackageName = defaultAppProvider.getDefaultDialer(userId);
+ final String requiredInstallerPackage = getKnownPackageName(PACKAGE_INSTALLER, userId);
+ final String requiredUninstallerPackage =
+ getKnownPackageName(PACKAGE_UNINSTALLER, userId);
+ final String requiredVerifierPackage = getKnownPackageName(PACKAGE_VERIFIER, userId);
+ final String requiredPermissionControllerPackage =
+ getKnownPackageName(PACKAGE_PERMISSION_CONTROLLER, userId);
+ for (int i = 0; i < packageNames.length; i++) {
+ canSuspend[i] = false;
+ final String packageName = packageNames[i];
+
+ if (mPm.isPackageDeviceAdmin(packageName, userId)) {
+ Slog.w(TAG, "Cannot suspend package \"" + packageName
+ + "\": has an active device admin");
+ continue;
+ }
+ if (packageName.equals(activeLauncherPackageName)) {
+ Slog.w(TAG, "Cannot suspend package \"" + packageName
+ + "\": contains the active launcher");
+ continue;
+ }
+ if (packageName.equals(requiredInstallerPackage)) {
+ Slog.w(TAG, "Cannot suspend package \"" + packageName
+ + "\": required for package installation");
+ continue;
+ }
+ if (packageName.equals(requiredUninstallerPackage)) {
+ Slog.w(TAG, "Cannot suspend package \"" + packageName
+ + "\": required for package uninstallation");
+ continue;
+ }
+ if (packageName.equals(requiredVerifierPackage)) {
+ Slog.w(TAG, "Cannot suspend package \"" + packageName
+ + "\": required for package verification");
+ continue;
+ }
+ if (packageName.equals(dialerPackageName)) {
+ Slog.w(TAG, "Cannot suspend package \"" + packageName
+ + "\": is the default dialer");
+ continue;
+ }
+ if (packageName.equals(requiredPermissionControllerPackage)) {
+ Slog.w(TAG, "Cannot suspend package \"" + packageName
+ + "\": required for permissions management");
+ continue;
+ }
+ synchronized (mPm.mLock) {
+ if (mProtectedPackages.isPackageStateProtected(userId, packageName)) {
+ Slog.w(TAG, "Cannot suspend package \"" + packageName
+ + "\": protected package");
+ continue;
+ }
+ if (!isCallerOwner && mPm.mSettings.getBlockUninstallLPr(userId, packageName)) {
+ Slog.w(TAG, "Cannot suspend package \"" + packageName
+ + "\": blocked by admin");
+ continue;
+ }
+
+ AndroidPackage pkg = mPm.mPackages.get(packageName);
+ if (pkg != null) {
+ // Cannot suspend SDK libs as they are controlled by SDK manager.
+ if (pkg.isSdkLibrary()) {
+ Slog.w(TAG, "Cannot suspend package: " + packageName
+ + " providing SDK library: "
+ + pkg.getSdkLibName());
+ continue;
+ }
+ // Cannot suspend static shared libs as they are considered
+ // a part of the using app (emulating static linking). Also
+ // static libs are installed always on internal storage.
+ if (pkg.isStaticSharedLibrary()) {
+ Slog.w(TAG, "Cannot suspend package: " + packageName
+ + " providing static shared library: "
+ + pkg.getStaticSharedLibName());
+ continue;
+ }
+ }
+ }
+ if (PLATFORM_PACKAGE_NAME.equals(packageName)) {
+ Slog.w(TAG, "Cannot suspend the platform package: " + packageName);
+ continue;
+ }
+ canSuspend[i] = true;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ return canSuspend;
+ }
+
+ /**
+ * Send broadcast intents for packages suspension changes.
+ *
+ * @param intent The action name of the suspension intent.
+ * @param pkgList The names of packages which have suspension changes.
+ * @param uidList The uids of packages which have suspension changes.
+ * @param userId The user where packages reside.
+ */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ void sendPackagesSuspendedForUser(@NonNull String intent, @NonNull String[] pkgList,
+ @NonNull int[] uidList, int userId) {
+ final List<List<String>> pkgsToSend = new ArrayList(pkgList.length);
+ final List<IntArray> uidsToSend = new ArrayList(pkgList.length);
+ final List<SparseArray<int[]>> allowListsToSend = new ArrayList(pkgList.length);
+ final int[] userIds = new int[] {userId};
+ // Get allow lists for the pkg in the pkgList. Merge into the existed pkgs and uids if
+ // allow lists are the same.
+ for (int i = 0; i < pkgList.length; i++) {
+ final String pkgName = pkgList[i];
+ final int uid = uidList[i];
+ SparseArray<int[]> allowList = mInjector.getAppsFilter().getVisibilityAllowList(
+ mPm.getPackageStateInternal(pkgName, SYSTEM_UID),
+ userIds, mPm.getPackageStates());
+ if (allowList == null) {
+ allowList = new SparseArray<>(0);
+ }
+ boolean merged = false;
+ for (int j = 0; j < allowListsToSend.size(); j++) {
+ if (Arrays.equals(allowListsToSend.get(j).get(userId), allowList.get(userId))) {
+ pkgsToSend.get(j).add(pkgName);
+ uidsToSend.get(j).add(uid);
+ merged = true;
+ break;
+ }
+ }
+ if (!merged) {
+ pkgsToSend.add(new ArrayList<>(Arrays.asList(pkgName)));
+ uidsToSend.add(IntArray.wrap(new int[] {uid}));
+ allowListsToSend.add(allowList);
+ }
+ }
+
+ final Handler handler = mInjector.getHandler();
+ for (int i = 0; i < pkgsToSend.size(); i++) {
+ final Bundle extras = new Bundle(3);
+ extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST,
+ pkgsToSend.get(i).toArray(new String[pkgsToSend.get(i).size()]));
+ extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidsToSend.get(i).toArray());
+ final SparseArray<int[]> allowList = allowListsToSend.get(i).size() == 0
+ ? null : allowListsToSend.get(i);
+ handler.post(() -> mBroadcastHelper.sendPackageBroadcast(intent, null /* pkg */,
+ extras, Intent.FLAG_RECEIVER_REGISTERED_ONLY, null /* targetPkg */,
+ null /* finishedReceiver */, userIds, null /* instantUserIds */,
+ allowList, null /* bOptions */));
+ }
+ }
+
+ private String getKnownPackageName(@KnownPackage int knownPackage, int userId) {
+ final String[] knownPackages = mPm.getKnownPackageNamesInternal(knownPackage, userId);
+ return knownPackages.length > 0 ? knownPackages[0] : null;
+ }
+
+ private boolean isCallerDeviceOrProfileOwner(int userId, int callingUid) {
+ if (callingUid == SYSTEM_UID) {
+ return true;
+ }
+ final String ownerPackage = mProtectedPackages.getDeviceOwnerOrProfileOwnerPackage(userId);
+ if (ownerPackage != null) {
+ return callingUid == mPm.getPackageUidInternal(
+ ownerPackage, 0, userId, callingUid);
+ }
+ return false;
+ }
+
+ private void sendMyPackageSuspendedOrUnsuspended(String[] affectedPackages, boolean suspended,
+ int userId) {
+ final Handler handler = mInjector.getHandler();
+ final String action = suspended
+ ? Intent.ACTION_MY_PACKAGE_SUSPENDED
+ : Intent.ACTION_MY_PACKAGE_UNSUSPENDED;
+ handler.post(() -> {
+ final IActivityManager am = ActivityManager.getService();
+ if (am == null) {
+ Slog.wtf(TAG, "IActivityManager null. Cannot send MY_PACKAGE_ "
+ + (suspended ? "" : "UN") + "SUSPENDED broadcasts");
+ return;
+ }
+ final int[] targetUserIds = new int[] {userId};
+ for (String packageName : affectedPackages) {
+ final Bundle appExtras = suspended
+ ? getSuspendedPackageAppExtras(packageName, userId, SYSTEM_UID)
+ : null;
+ final Bundle intentExtras;
+ if (appExtras != null) {
+ intentExtras = new Bundle(1);
+ intentExtras.putBundle(Intent.EXTRA_SUSPENDED_PACKAGE_EXTRAS, appExtras);
+ } else {
+ intentExtras = null;
+ }
+ handler.post(() -> mBroadcastHelper.doSendBroadcast(action, null, intentExtras,
+ Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, packageName, null,
+ targetUserIds, false, null, null));
+ }
+ });
+ }
+}
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index d29dbbc7c04a..d6e88f40d05d 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -3675,9 +3675,18 @@ public class UserManagerService extends IUserManager.Stub {
@UserInfoFlag int flags, @UserIdInt int parentId,
@Nullable String[] disallowedPackages)
throws UserManager.CheckedUserOperationException {
- String restriction = (UserManager.isUserTypeManagedProfile(userType))
- ? UserManager.DISALLOW_ADD_MANAGED_PROFILE
- : UserManager.DISALLOW_ADD_USER;
+
+ // Checking user restriction before creating new user,
+ // default check is for DISALLOW_ADD_USER
+ // If new user is of type CLONE, check if creation of clone profile is allowed
+ // If new user is of type MANAGED, check if creation of managed profile is allowed
+ String restriction = UserManager.DISALLOW_ADD_USER;
+ if (UserManager.isUserTypeCloneProfile(userType)) {
+ restriction = UserManager.DISALLOW_ADD_CLONE_PROFILE;
+ } else if (UserManager.isUserTypeManagedProfile(userType)) {
+ restriction = UserManager.DISALLOW_ADD_MANAGED_PROFILE;
+ }
+
enforceUserRestriction(restriction, UserHandle.getCallingUserId(),
"Cannot add user");
return createUserInternalUnchecked(name, userType, flags, parentId,
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 0f3b4bcfac56..1fa901352c3d 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -101,6 +101,7 @@ public class UserRestrictionsUtils {
UserManager.DISALLOW_FACTORY_RESET,
UserManager.DISALLOW_ADD_USER,
UserManager.DISALLOW_ADD_MANAGED_PROFILE,
+ UserManager.DISALLOW_ADD_CLONE_PROFILE,
UserManager.ENSURE_VERIFY_APPS,
UserManager.DISALLOW_CONFIG_CELL_BROADCASTS,
UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS,
diff --git a/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java b/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java
index 4bbe3733719e..d455be7e4a69 100644
--- a/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java
+++ b/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java
@@ -162,7 +162,10 @@ public class OneTimePermissionUserManager {
* The delay to wait before revoking on the event an app is terminated. Recommended to be long
* enough so that apps don't lose permission on an immediate restart
*/
- private static long getKilledDelayMillis() {
+ private long getKilledDelayMillis(boolean isSelfRevokedPermissionSession) {
+ if (isSelfRevokedPermissionSession) {
+ return 0;
+ }
return DeviceConfig.getLong(DeviceConfig.NAMESPACE_PERMISSIONS,
PROPERTY_KILLED_DELAY_CONFIG_KEY, DEFAULT_KILLED_DELAY_MILLIS);
}
@@ -175,6 +178,18 @@ public class OneTimePermissionUserManager {
mContext.registerReceiver(mUninstallListener, new IntentFilter(Intent.ACTION_UID_REMOVED));
}
+ void setSelfRevokedPermissionSession(int uid) {
+ synchronized (mLock) {
+ PackageInactivityListener listener = mListeners.get(uid);
+ if (listener == null) {
+ Log.e(LOG_TAG, "Could not set session for uid " + uid
+ + " as self-revoke session: session not found");
+ return;
+ }
+ listener.setSelfRevokedPermissionSession();
+ }
+ }
+
/**
* A class which watches a package for inactivity and notifies the permission controller when
* the package becomes inactive
@@ -189,6 +204,7 @@ public class OneTimePermissionUserManager {
private final int mImportanceToResetTimer;
private final int mImportanceToKeepSessionAlive;
+ private boolean mIsSelfRevokedPermissionSession;
private boolean mIsAlarmSet;
private boolean mIsFinished;
@@ -255,7 +271,7 @@ public class OneTimePermissionUserManager {
}
onImportanceChanged(mUid, imp);
}
- }, mToken, getKilledDelayMillis());
+ }, mToken, getKilledDelayMillis(mIsSelfRevokedPermissionSession));
return;
}
if (importance > mImportanceToResetTimer) {
@@ -291,6 +307,14 @@ public class OneTimePermissionUserManager {
}
/**
+ * Marks the session as a self-revoke session, which does not delay the revocation when
+ * the app is restarting.
+ */
+ public void setSelfRevokedPermissionSession() {
+ mIsSelfRevokedPermissionSession = true;
+ }
+
+ /**
* Set the alarm which will callback when the package is inactive
*/
@GuardedBy("mInnerLock")
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 1cfcdf51f5b8..317730a9f606 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -66,6 +66,7 @@ import android.util.Slog;
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.infra.AndroidFuture;
import com.android.internal.util.Preconditions;
import com.android.internal.util.function.TriFunction;
import com.android.server.LocalServices;
@@ -558,9 +559,18 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
@Override
- public void selfRevokePermissions(@NonNull String packageName,
+ public void revokeOwnPermissionsOnKill(@NonNull String packageName,
@NonNull List<String> permissions) {
- mPermissionManagerServiceImpl.selfRevokePermissions(packageName, permissions);
+ final int callingUid = Binder.getCallingUid();
+ final int callingUserId = UserHandle.getUserId(callingUid);
+ AndroidFuture<Void> future = new AndroidFuture<>();
+ future.whenComplete((result, err) -> {
+ if (err == null) {
+ getOneTimePermissionUserManager(callingUserId)
+ .setSelfRevokedPermissionSession(callingUid);
+ }
+ });
+ mPermissionManagerServiceImpl.revokeOwnPermissionsOnKill(packageName, permissions, future);
}
@Override
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
index 981fd8e9e789..9b3d6d6eb3de 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
@@ -113,6 +113,7 @@ import android.util.SparseBooleanArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.compat.IPlatformCompat;
+import com.android.internal.infra.AndroidFuture;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.os.RoSystemProperties;
@@ -1592,7 +1593,8 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt
}
@Override
- public void selfRevokePermissions(String packageName, List<String> permissions) {
+ public void revokeOwnPermissionsOnKill(String packageName, List<String> permissions,
+ AndroidFuture<Void> callback) {
final int callingUid = Binder.getCallingUid();
int callingUserId = UserHandle.getUserId(callingUid);
int targetPackageUid = mPackageManagerInt.getPackageUid(packageName, 0, callingUserId);
@@ -1607,7 +1609,8 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt
+ permName + " because it does not hold that permission");
}
}
- mPermissionControllerManager.selfRevokePermissions(packageName, permissions);
+ mPermissionControllerManager.revokeOwnPermissionsOnKill(packageName, permissions,
+ callback);
}
private boolean mayManageRolePermission(int uid) {
@@ -3181,9 +3184,13 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt
ps.updatePermissionFlags(bp, PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED
| FLAG_PERMISSION_REVOKE_WHEN_REQUESTED,
PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED);
- // TODO(b/205888750): remove revoke once propagated through droidfood
- if (ps.isPermissionGranted(newPerm)) {
+ // TODO(b/205888750): remove if/else block once propagated through droidfood
+ if (ps.isPermissionGranted(newPerm)
+ && pkg.getTargetSdkVersion() >= Build.VERSION_CODES.M) {
ps.revokePermission(bp);
+ } else if (!ps.isPermissionGranted(newPerm)
+ && pkg.getTargetSdkVersion() < Build.VERSION_CODES.M) {
+ ps.grantPermission(bp);
}
}
}
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java
index c582f9efa7a0..91c558b2f35e 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java
@@ -27,6 +27,7 @@ import android.content.pm.permission.SplitPermissionInfoParcelable;
import android.permission.IOnPermissionsChangeListener;
import android.permission.PermissionManagerInternal;
+import com.android.internal.infra.AndroidFuture;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import java.io.FileDescriptor;
@@ -343,8 +344,10 @@ public interface PermissionManagerServiceInterface extends PermissionManagerInte
*
* @param packageName The name of the package for which the permissions will be revoked.
* @param permissions List of permissions to be revoked.
+ * @param callback Callback called when the revocation request has been completed.
*/
- void selfRevokePermissions(String packageName, List<String> permissions);
+ void revokeOwnPermissionsOnKill(String packageName, List<String> permissions,
+ AndroidFuture<Void> callback);
/**
* Get whether you should show UI with rationale for requesting a permission. You should do this
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java
index 18a6435d17c8..52d9b7a3abc1 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java
@@ -26,6 +26,10 @@ import android.content.pm.FeatureGroupInfo;
import android.content.pm.FeatureInfo;
import android.content.pm.PackageManager.Property;
import android.content.pm.SigningDetails;
+import android.os.Bundle;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
+
import com.android.server.pm.pkg.component.ParsedActivity;
import com.android.server.pm.pkg.component.ParsedApexSystemService;
import com.android.server.pm.pkg.component.ParsedAttribution;
@@ -37,9 +41,6 @@ import com.android.server.pm.pkg.component.ParsedProcess;
import com.android.server.pm.pkg.component.ParsedProvider;
import com.android.server.pm.pkg.component.ParsedService;
import com.android.server.pm.pkg.component.ParsedUsesPermission;
-import android.os.Bundle;
-import android.util.SparseArray;
-import android.util.SparseIntArray;
import java.security.PublicKey;
import java.util.Map;
@@ -286,6 +287,9 @@ public interface ParsingPackage extends ParsingPackageRead {
ParsingPackage setInstallLocation(int installLocation);
+ /** @see R#styleable.AndroidManifest_inheritKeyStoreKeys */
+ ParsingPackage setInheritKeyStoreKeys(boolean inheritKeyStoreKeys);
+
ParsingPackage setLabelRes(int labelRes);
ParsingPackage setLargestWidthLimitDp(int largestWidthLimitDp);
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageImpl.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageImpl.java
index c4de862bccd9..1f21938fc706 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageImpl.java
@@ -492,6 +492,10 @@ public class ParsingPackageImpl implements ParsingPackage, ParsingPackageHidden,
ENABLED,
DISALLOW_PROFILING,
REQUEST_FOREGROUND_SERVICE_EXEMPTION,
+ ATTRIBUTIONS_ARE_USER_VISIBLE,
+ RESET_ENABLED_SETTINGS_ON_APP_DATA_CLEARED,
+ SDK_LIBRARY,
+ INHERIT_KEYSTORE_KEYS,
})
public @interface Values {}
private static final long EXTERNAL_STORAGE = 1L;
@@ -544,6 +548,7 @@ public class ParsingPackageImpl implements ParsingPackage, ParsingPackageHidden,
private static final long ATTRIBUTIONS_ARE_USER_VISIBLE = 1L << 47;
private static final long RESET_ENABLED_SETTINGS_ON_APP_DATA_CLEARED = 1L << 48;
private static final long SDK_LIBRARY = 1L << 49;
+ private static final long INHERIT_KEYSTORE_KEYS = 1L << 50;
}
private ParsingPackageImpl setBoolean(@Booleans.Values long flag, boolean value) {
@@ -2371,6 +2376,11 @@ public class ParsingPackageImpl implements ParsingPackage, ParsingPackageHidden,
}
@Override
+ public boolean shouldInheritKeyStoreKeys() {
+ return getBoolean(Booleans.INHERIT_KEYSTORE_KEYS);
+ }
+
+ @Override
public ParsingPackageImpl setBaseRevisionCode(int value) {
baseRevisionCode = value;
return this;
@@ -2514,6 +2524,11 @@ public class ParsingPackageImpl implements ParsingPackage, ParsingPackageHidden,
}
@Override
+ public ParsingPackageImpl setInheritKeyStoreKeys(boolean value) {
+ return setBoolean(Booleans.INHERIT_KEYSTORE_KEYS, value);
+ }
+
+ @Override
public ParsingPackageImpl setLabelRes(int value) {
labelRes = value;
return this;
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageRead.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageRead.java
index 149711287f32..4b659a14418f 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageRead.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageRead.java
@@ -350,4 +350,9 @@ public interface ParsingPackageRead extends PkgWithoutStateAppInfo, PkgWithoutSt
* @see R.styleable#AndroidManifestApplication_localeConfig
*/
int getLocaleConfigRes();
+
+ /**
+ * @see R.styleable#AndroidManifest_inheritKeyStoreKeys
+ */
+ boolean shouldInheritKeyStoreKeys();
}
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
index 1ce01f633791..bf7c55f0f59e 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
@@ -868,7 +868,9 @@ public class ParsingPackageUtils {
.setTargetSandboxVersion(anInteger(PARSE_DEFAULT_TARGET_SANDBOX,
R.styleable.AndroidManifest_targetSandboxVersion, sa))
/* Set the global "on SD card" flag */
- .setExternalStorage((flags & PARSE_EXTERNAL_STORAGE) != 0);
+ .setExternalStorage((flags & PARSE_EXTERNAL_STORAGE) != 0)
+ .setInheritKeyStoreKeys(bool(false,
+ R.styleable.AndroidManifest_inheritKeyStoreKeys, sa));
boolean foundApp = false;
final int depth = parser.getDepth();
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 28f65cf6d1a0..7dd942507a16 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -5401,18 +5401,18 @@ public class PhoneWindowManager implements WindowManagerPolicy {
if (!mVibrator.hasVibrator()) {
return false;
}
- final boolean hapticsDisabled = Settings.System.getIntForUser(mContext.getContentResolver(),
- Settings.System.HAPTIC_FEEDBACK_ENABLED, 0, UserHandle.USER_CURRENT) == 0;
- if (hapticsDisabled && !always) {
- return false;
- }
-
VibrationEffect effect = getVibrationEffect(effectId);
if (effect == null) {
return false;
}
-
- mVibrator.vibrate(uid, packageName, effect, reason, getVibrationAttributes(effectId));
+ VibrationAttributes attrs = getVibrationAttributes(effectId);
+ if (always) {
+ attrs = new VibrationAttributes.Builder(attrs)
+ .setFlags(VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF,
+ VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF)
+ .build();
+ }
+ mVibrator.vibrate(uid, packageName, effect, reason, attrs);
return true;
}
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index 16176f026578..b7ca4defda5f 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -121,7 +121,6 @@ import android.os.IThermalService;
import android.os.OutcomeReceiver;
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
-import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceSpecificException;
@@ -231,7 +230,6 @@ import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@@ -2332,11 +2330,7 @@ public class StatsPullAtomService extends SystemService {
List<ProcessMemoryState> managedProcessList =
LocalServices.getService(ActivityManagerInternal.class)
.getMemoryStateForProcesses();
- managedProcessList.sort(Comparator.comparingInt(x -> x.oomScore));
for (ProcessMemoryState process : managedProcessList) {
- if (process.uid == Process.SYSTEM_UID) {
- continue;
- }
KernelAllocationStats.ProcessDmabuf proc =
KernelAllocationStats.getDmabufAllocations(process.pid);
if (proc == null || (proc.retainedBuffersCount <= 0 && proc.mappedBuffersCount <= 0)) {
@@ -2353,6 +2347,32 @@ public class StatsPullAtomService extends SystemService {
proc.mappedSizeKb,
proc.mappedBuffersCount));
}
+ SparseArray<String> processCmdlines = getProcessCmdlines();
+ managedProcessList.forEach(managedProcess -> processCmdlines.delete(managedProcess.pid));
+ int size = processCmdlines.size();
+ for (int i = 0; i < size; ++i) {
+ int pid = processCmdlines.keyAt(i);
+ int uid = getUidForPid(pid);
+ // ignore root processes (unlikely to be interesting)
+ if (uid <= 0) {
+ continue;
+ }
+ KernelAllocationStats.ProcessDmabuf proc =
+ KernelAllocationStats.getDmabufAllocations(pid);
+ if (proc == null || (proc.retainedBuffersCount <= 0 && proc.mappedBuffersCount <= 0)) {
+ continue;
+ }
+ pulledData.add(
+ FrameworkStatsLog.buildStatsEvent(
+ atomTag,
+ uid,
+ processCmdlines.valueAt(i),
+ -1001 /*Placeholder for native processes, OOM_SCORE_ADJ_MIN - 1.*/,
+ proc.retainedSizeKb,
+ proc.retainedBuffersCount,
+ proc.mappedSizeKb,
+ proc.mappedBuffersCount));
+ }
return StatsManager.PULL_SUCCESS;
}
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 17f5566a9864..0edd06acd7e9 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -22,6 +22,7 @@ import static android.app.StatusBarManager.NAV_BAR_MODE_OVERRIDE_KIDS;
import static android.app.StatusBarManager.NAV_BAR_MODE_OVERRIDE_NONE;
import static android.app.StatusBarManager.NavBarModeOverride;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY;
import android.Manifest;
import android.annotation.NonNull;
@@ -38,6 +39,7 @@ import android.compat.annotation.EnabledSince;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.om.IOverlayManager;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ResolveInfo;
@@ -60,6 +62,7 @@ import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
import android.os.ResultReceiver;
+import android.os.ServiceManager;
import android.os.ShellCallback;
import android.os.UserHandle;
import android.provider.Settings;
@@ -79,6 +82,7 @@ import android.view.WindowInsetsController.Behavior;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.inputmethod.SoftInputShowHideReason;
import com.android.internal.os.TransferPipe;
import com.android.internal.statusbar.IAddTileResultCallback;
@@ -154,6 +158,8 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
private final ArrayMap<String, Long> mCurrentRequestAddTilePackages = new ArrayMap<>();
private static final long REQUEST_TIME_OUT = TimeUnit.MINUTES.toNanos(5);
+ private IOverlayManager mOverlayManager;
+
private class DeathRecipient implements IBinder.DeathRecipient {
public void binderDied() {
mBar.asBinder().unlinkToDeath(this,0);
@@ -256,6 +262,18 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
mTileRequestTracker = new TileRequestTracker(mContext);
}
+ private IOverlayManager getOverlayManager() {
+ // No need to synchronize; worst-case scenario it will be fetched twice.
+ if (mOverlayManager == null) {
+ mOverlayManager = IOverlayManager.Stub.asInterface(
+ ServiceManager.getService(Context.OVERLAY_SERVICE));
+ if (mOverlayManager == null) {
+ Slog.w("StatusBarManager", "warning: no OVERLAY_SERVICE");
+ }
+ }
+ return mOverlayManager;
+ }
+
@Override
public void onDisplayAdded(int displayId) {}
@@ -1296,6 +1314,11 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
});
}
+ @VisibleForTesting
+ void registerOverlayManager(IOverlayManager overlayManager) {
+ mOverlayManager = overlayManager;
+ }
+
/**
* @param clearNotificationEffects whether to consider notifications as "shown" and stop
* LED, vibration, and ringing
@@ -1869,6 +1892,14 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
try {
Settings.Secure.putIntForUser(mContext.getContentResolver(),
Settings.Secure.NAV_BAR_KIDS_MODE, navBarModeOverride, userId);
+
+ IOverlayManager overlayManager = getOverlayManager();
+ if (overlayManager != null && navBarModeOverride == NAV_BAR_MODE_OVERRIDE_KIDS
+ && isPackageSupported(NAV_BAR_MODE_3BUTTON_OVERLAY)) {
+ overlayManager.setEnabledExclusiveInCategory(NAV_BAR_MODE_3BUTTON_OVERLAY, userId);
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
} finally {
Binder.restoreCallingIdentity(userIdentity);
}
@@ -1896,6 +1927,21 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
return navBarKidsMode;
}
+ private boolean isPackageSupported(String packageName) {
+ if (packageName == null) {
+ return false;
+ }
+ try {
+ return mContext.getPackageManager().getPackageInfo(packageName,
+ PackageManager.PackageInfoFlags.of(0)) != null;
+ } catch (PackageManager.NameNotFoundException ignored) {
+ if (SPEW) {
+ Slog.d(TAG, "Package not found: " + packageName);
+ }
+ }
+ return false;
+ }
+
/** @hide */
public void passThroughShellCommand(String[] args, FileDescriptor fd) {
enforceStatusBarOrShell();
diff --git a/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java b/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java
index 3093509428d3..c0207f044b83 100644
--- a/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java
+++ b/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java
@@ -37,6 +37,7 @@ import android.os.ShellCommand;
import android.os.UserHandle;
import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
+import android.provider.DeviceConfig;
import android.text.format.DateUtils;
import android.util.ArrayMap;
import android.util.DataUnit;
@@ -255,11 +256,14 @@ public class DeviceStorageMonitorService extends SystemService {
private void checkHigh() {
final StorageManager storage = getContext().getSystemService(StorageManager.class);
// Check every mounted private volume to see if they're under the high storage threshold
- // which is StorageManager.STORAGE_THRESHOLD_PERCENT_HIGH of total space
+ // which is storageThresholdPercentHigh of total space
+ final int storageThresholdPercentHigh = DeviceConfig.getInt(
+ DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+ StorageManager.STORAGE_THRESHOLD_PERCENT_HIGH_KEY,
+ StorageManager.DEFAULT_STORAGE_THRESHOLD_PERCENT_HIGH);
for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
final File file = vol.getPath();
- if (file.getUsableSpace() < file.getTotalSpace()
- * StorageManager.STORAGE_THRESHOLD_PERCENT_HIGH / 100) {
+ if (file.getUsableSpace() < file.getTotalSpace() * storageThresholdPercentHigh / 100) {
final PackageManagerService pms = (PackageManagerService) ServiceManager
.getService("package");
try {
diff --git a/services/core/java/com/android/server/trust/TrustAgentWrapper.java b/services/core/java/com/android/server/trust/TrustAgentWrapper.java
index 79231f709738..06ce4a41afec 100644
--- a/services/core/java/com/android/server/trust/TrustAgentWrapper.java
+++ b/services/core/java/com/android/server/trust/TrustAgentWrapper.java
@@ -16,6 +16,8 @@
package com.android.server.trust;
+import static android.service.trust.TrustAgentService.FLAG_GRANT_TRUST_DISPLAY_MESSAGE;
+
import android.annotation.TargetApi;
import android.app.AlarmManager;
import android.app.PendingIntent;
@@ -99,6 +101,7 @@ public class TrustAgentWrapper {
// Trust state
private boolean mTrusted;
private CharSequence mMessage;
+ private boolean mDisplayTrustGrantedMessage;
private boolean mTrustDisabledByDpm;
private boolean mManagingTrust;
private IBinder mSetTrustAgentFeaturesToken;
@@ -132,6 +135,7 @@ public class TrustAgentWrapper {
mTrusted = true;
mMessage = (CharSequence) msg.obj;
int flags = msg.arg1;
+ mDisplayTrustGrantedMessage = (flags & FLAG_GRANT_TRUST_DISPLAY_MESSAGE) != 0;
long durationMs = msg.getData().getLong(DATA_DURATION);
if (durationMs > 0) {
final long duration;
@@ -166,6 +170,7 @@ public class TrustAgentWrapper {
// Fall through.
case MSG_REVOKE_TRUST:
mTrusted = false;
+ mDisplayTrustGrantedMessage = false;
mMessage = null;
mHandler.removeMessages(MSG_TRUST_TIMEOUT);
if (msg.what == MSG_REVOKE_TRUST) {
@@ -199,6 +204,7 @@ public class TrustAgentWrapper {
mManagingTrust = msg.arg1 != 0;
if (!mManagingTrust) {
mTrusted = false;
+ mDisplayTrustGrantedMessage = false;
mMessage = null;
}
mTrustManagerService.mArchive.logManagingTrust(mUserId, mName, mManagingTrust);
@@ -271,12 +277,13 @@ public class TrustAgentWrapper {
private ITrustAgentServiceCallback mCallback = new ITrustAgentServiceCallback.Stub() {
@Override
- public void grantTrust(CharSequence userMessage, long durationMs, int flags) {
- if (DEBUG) Slog.d(TAG, "enableTrust(" + userMessage + ", durationMs = " + durationMs
+ public void grantTrust(CharSequence message, long durationMs, int flags) {
+ if (DEBUG) {
+ Slog.d(TAG, "enableTrust(" + message + ", durationMs = " + durationMs
+ ", flags = " + flags + ")");
+ }
- Message msg = mHandler.obtainMessage(
- MSG_GRANT_TRUST, flags, 0, userMessage);
+ Message msg = mHandler.obtainMessage(MSG_GRANT_TRUST, flags, 0, message);
msg.getData().putLong(DATA_DURATION, durationMs);
msg.sendToTarget();
}
@@ -461,6 +468,19 @@ public class TrustAgentWrapper {
}
/**
+ * @see android.service.trust.TrustAgentService#onUserRequestedUnlock()
+ */
+ public void onUserRequestedUnlock() {
+ try {
+ if (mTrustAgentService != null) {
+ mTrustAgentService.onUserRequestedUnlock();
+ }
+ } catch (RemoteException e) {
+ onError(e);
+ }
+ }
+
+ /**
* @see android.service.trust.TrustAgentService#onUnlockLockout(int)
*/
public void onUnlockLockout(int timeoutMs) {
@@ -579,6 +599,14 @@ public class TrustAgentWrapper {
return mMessage;
}
+ /**
+ * Whether the trust agent would like to display {@link #getMessage()} to the user when trust
+ * is granted.
+ */
+ public boolean shouldDisplayTrustGrantedMessage() {
+ return mDisplayTrustGrantedMessage;
+ }
+
public void destroy() {
mHandler.removeMessages(MSG_RESTART_TIMEOUT);
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index fc87253825f4..9bed24d05f3d 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -75,7 +75,6 @@ import com.android.internal.content.PackageMonitor;
import com.android.internal.util.DumpUtils;
import com.android.internal.widget.LockPatternUtils;
import com.android.server.SystemService;
-import com.android.server.SystemService.TargetUser;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -123,6 +122,7 @@ public class TrustManagerService extends SystemService {
private static final int MSG_DISPATCH_UNLOCK_LOCKOUT = 13;
private static final int MSG_REFRESH_DEVICE_LOCKED_FOR_USER = 14;
private static final int MSG_SCHEDULE_TRUST_TIMEOUT = 15;
+ public static final int MSG_USER_REQUESTED_UNLOCK = 16;
private static final String REFRESH_DEVICE_LOCKED_EXCEPT_USER = "except";
@@ -391,7 +391,6 @@ public class TrustManagerService extends SystemService {
}
}
-
public void updateTrust(int userId, int flags) {
updateTrust(userId, flags, false /* isFromUnlock */);
}
@@ -431,7 +430,7 @@ public class TrustManagerService extends SystemService {
changed = mUserIsTrusted.get(userId) != trusted;
mUserIsTrusted.put(userId, trusted);
}
- dispatchOnTrustChanged(trusted, userId, flags);
+ dispatchOnTrustChanged(trusted, userId, flags, getTrustGrantedMessages(userId));
if (changed) {
refreshDeviceLockedForUser(userId);
if (!trusted) {
@@ -951,6 +950,24 @@ public class TrustManagerService extends SystemService {
return false;
}
+ private List<String> getTrustGrantedMessages(int userId) {
+ if (!mStrongAuthTracker.isTrustAllowedForUser(userId)) {
+ return new ArrayList<>();
+ }
+
+ List<String> trustGrantedMessages = new ArrayList<>();
+ for (int i = 0; i < mActiveAgents.size(); i++) {
+ AgentInfo info = mActiveAgents.valueAt(i);
+ if (info.userId == userId
+ && info.agent.isTrusted()
+ && info.agent.shouldDisplayTrustGrantedMessage()
+ && !TextUtils.isEmpty(info.agent.getMessage())) {
+ trustGrantedMessages.add(info.agent.getMessage().toString());
+ }
+ }
+ return trustGrantedMessages;
+ }
+
private boolean aggregateIsTrustManaged(int userId) {
if (!mStrongAuthTracker.isTrustAllowedForUser(userId)) {
return false;
@@ -981,6 +998,15 @@ public class TrustManagerService extends SystemService {
}
}
+ private void dispatchUserRequestedUnlock(int userId) {
+ for (int i = 0; i < mActiveAgents.size(); i++) {
+ AgentInfo info = mActiveAgents.valueAt(i);
+ if (info.userId == userId) {
+ info.agent.onUserRequestedUnlock();
+ }
+ }
+ }
+
private void dispatchUnlockLockout(int timeoutMs, int userId) {
for (int i = 0; i < mActiveAgents.size(); i++) {
AgentInfo info = mActiveAgents.valueAt(i);
@@ -1011,7 +1037,8 @@ public class TrustManagerService extends SystemService {
}
}
- private void dispatchOnTrustChanged(boolean enabled, int userId, int flags) {
+ private void dispatchOnTrustChanged(boolean enabled, int userId, int flags,
+ @NonNull List<String> trustGrantedMessages) {
if (DEBUG) {
Log.i(TAG, "onTrustChanged(" + enabled + ", " + userId + ", 0x"
+ Integer.toHexString(flags) + ")");
@@ -1019,7 +1046,7 @@ public class TrustManagerService extends SystemService {
if (!enabled) flags = 0;
for (int i = 0; i < mTrustListeners.size(); i++) {
try {
- mTrustListeners.get(i).onTrustChanged(enabled, userId, flags);
+ mTrustListeners.get(i).onTrustChanged(enabled, userId, flags, trustGrantedMessages);
} catch (DeadObjectException e) {
Slog.d(TAG, "Removing dead TrustListener.");
mTrustListeners.remove(i);
@@ -1110,6 +1137,12 @@ public class TrustManagerService extends SystemService {
}
@Override
+ public void reportUserRequestedUnlock(int userId) throws RemoteException {
+ enforceReportPermission();
+ mHandler.obtainMessage(MSG_USER_REQUESTED_UNLOCK, userId).sendToTarget();
+ }
+
+ @Override
public void reportUnlockLockout(int timeoutMs, int userId) throws RemoteException {
enforceReportPermission();
mHandler.obtainMessage(MSG_DISPATCH_UNLOCK_LOCKOUT, timeoutMs, userId)
@@ -1389,6 +1422,9 @@ public class TrustManagerService extends SystemService {
case MSG_DISPATCH_UNLOCK_ATTEMPT:
dispatchUnlockAttempt(msg.arg1 != 0, msg.arg2);
break;
+ case MSG_USER_REQUESTED_UNLOCK:
+ dispatchUserRequestedUnlock(msg.arg1);
+ break;
case MSG_DISPATCH_UNLOCK_LOCKOUT:
dispatchUnlockLockout(msg.arg1, msg.arg2);
break;
diff --git a/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java b/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java
index 6058d8873e94..b3649a75ee5b 100644
--- a/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java
+++ b/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java
@@ -34,15 +34,16 @@ import android.media.tv.AdResponse;
import android.media.tv.BroadcastInfoRequest;
import android.media.tv.BroadcastInfoResponse;
import android.media.tv.TvTrackInfo;
-import android.media.tv.interactive.ITvIAppManager;
+import android.media.tv.interactive.AppLinkInfo;
import android.media.tv.interactive.ITvInteractiveAppClient;
+import android.media.tv.interactive.ITvInteractiveAppManager;
import android.media.tv.interactive.ITvInteractiveAppManagerCallback;
import android.media.tv.interactive.ITvInteractiveAppService;
import android.media.tv.interactive.ITvInteractiveAppServiceCallback;
import android.media.tv.interactive.ITvInteractiveAppSession;
import android.media.tv.interactive.ITvInteractiveAppSessionCallback;
-import android.media.tv.interactive.TvIAppService;
import android.media.tv.interactive.TvInteractiveAppInfo;
+import android.media.tv.interactive.TvInteractiveAppService;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
@@ -79,9 +80,9 @@ import java.util.Set;
/**
* This class provides a system service that manages interactive TV applications.
*/
-public class TvIAppManagerService extends SystemService {
+public class TvInteractiveAppManagerService extends SystemService {
private static final boolean DEBUG = false;
- private static final String TAG = "TvIAppManagerService";
+ private static final String TAG = "TvInteractiveAppManagerService";
// A global lock.
private final Object mLock = new Object();
private final Context mContext;
@@ -95,6 +96,10 @@ public class TvIAppManagerService extends SystemService {
@GuardedBy("mLock")
private final SparseArray<UserState> mUserStates = new SparseArray<>();
+ // TODO: remove mGetServiceListCalled if onBootPhrase work correctly
+ @GuardedBy("mLock")
+ private boolean mGetServiceListCalled = false;
+
private final UserManager mUserManager;
/**
@@ -106,7 +111,7 @@ public class TvIAppManagerService extends SystemService {
*
* @param context The system server context.
*/
- public TvIAppManagerService(Context context) {
+ public TvInteractiveAppManagerService(Context context) {
super(context);
mContext = context;
mUserManager = (UserManager) getContext().getSystemService(Context.USER_SERVICE);
@@ -122,7 +127,7 @@ public class TvIAppManagerService extends SystemService {
}
PackageManager pm = mContext.getPackageManager();
List<ResolveInfo> services = pm.queryIntentServicesAsUser(
- new Intent(TvIAppService.SERVICE_INTERFACE),
+ new Intent(TvInteractiveAppService.SERVICE_INTERFACE),
PackageManager.GET_SERVICES | PackageManager.GET_META_DATA,
userId);
List<TvInteractiveAppInfo> iAppList = new ArrayList<>();
@@ -256,15 +261,16 @@ public class TvIAppManagerService extends SystemService {
@GuardedBy("mLock")
private void notifyStateChangedLocked(
- UserState userState, String iAppServiceId, int type, int state) {
+ UserState userState, String iAppServiceId, int type, int state, int err) {
if (DEBUG) {
Slog.d(TAG, "notifyRteStateChanged(iAppServiceId="
- + iAppServiceId + ", type=" + type + ", state=" + state + ")");
+ + iAppServiceId + ", type=" + type + ", state=" + state + ", err=" + err + ")");
}
int n = userState.mCallbacks.beginBroadcast();
for (int i = 0; i < n; ++i) {
try {
- userState.mCallbacks.getBroadcastItem(i).onStateChanged(iAppServiceId, type, state);
+ userState.mCallbacks.getBroadcastItem(i)
+ .onStateChanged(iAppServiceId, type, state, err);
} catch (RemoteException e) {
Slog.e(TAG, "failed to report RTE state changed", e);
}
@@ -287,7 +293,7 @@ public class TvIAppManagerService extends SystemService {
if (DEBUG) {
Slogf.d(TAG, "onStart");
}
- publishBinderService(Context.TV_IAPP_SERVICE, new BinderService());
+ publishBinderService(Context.TV_INTERACTIVE_APP_SERVICE, new BinderService());
}
@Override
@@ -628,7 +634,7 @@ public class TvIAppManagerService extends SystemService {
return session;
}
- private final class BinderService extends ITvIAppManager.Stub {
+ private final class BinderService extends ITvInteractiveAppManager.Stub {
@Override
public List<TvInteractiveAppInfo> getTvInteractiveAppServiceList(int userId) {
@@ -637,6 +643,10 @@ public class TvIAppManagerService extends SystemService {
final long identity = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
+ if (!mGetServiceListCalled) {
+ buildTvInteractiveAppServiceListLocked(userId, null);
+ mGetServiceListCalled = true;
+ }
UserState userState = getOrCreateUserStateLocked(resolvedUserId);
List<TvInteractiveAppInfo> iAppList = new ArrayList<>();
for (TvInteractiveAppState state : userState.mIAppMap.values()) {
@@ -686,9 +696,9 @@ public class TvIAppManagerService extends SystemService {
}
@Override
- public void registerAppLinkInfo(String tiasId, Bundle appLinkInfo, int userId) {
+ public void registerAppLinkInfo(String tiasId, AppLinkInfo appLinkInfo, int userId) {
final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
- Binder.getCallingUid(), userId, "registerAppLinkInfo");
+ Binder.getCallingUid(), userId, "registerAppLinkInfo: " + appLinkInfo);
final long identity = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
@@ -722,9 +732,9 @@ public class TvIAppManagerService extends SystemService {
}
@Override
- public void unregisterAppLinkInfo(String tiasId, Bundle appLinkInfo, int userId) {
+ public void unregisterAppLinkInfo(String tiasId, AppLinkInfo appLinkInfo, int userId) {
final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
- Binder.getCallingUid(), userId, "unregisterAppLinkInfo");
+ Binder.getCallingUid(), userId, "unregisterAppLinkInfo: " + appLinkInfo);
final long identity = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
@@ -1361,7 +1371,7 @@ public class TvIAppManagerService extends SystemService {
}
} finally {
if (surface != null) {
- // surface is not used in TvIAppManagerService.
+ // surface is not used in TvInteractiveAppManagerService.
surface.release();
}
Binder.restoreCallingIdentity(identity);
@@ -1678,7 +1688,7 @@ public class TvIAppManagerService extends SystemService {
}
Intent i =
- new Intent(TvIAppService.SERVICE_INTERFACE).setComponent(component);
+ new Intent(TvInteractiveAppService.SERVICE_INTERFACE).setComponent(component);
serviceState.mBound = mContext.bindServiceAsUser(
i, serviceState.mConnection,
Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
@@ -1810,7 +1820,7 @@ public class TvIAppManagerService extends SystemService {
private final ServiceConnection mConnection;
private final ComponentName mComponent;
private final String mIAppServiceId;
- private final List<Pair<Bundle, Boolean>> mPendingAppLinkInfo = new ArrayList<>();
+ private final List<Pair<AppLinkInfo, Boolean>> mPendingAppLinkInfo = new ArrayList<>();
private final List<Bundle> mPendingAppLinkCommand = new ArrayList<>();
private boolean mPendingPrepare = false;
@@ -1833,7 +1843,7 @@ public class TvIAppManagerService extends SystemService {
mIAppServiceId = tias;
}
- private void addPendingAppLink(Bundle info, boolean register) {
+ private void addPendingAppLink(AppLinkInfo info, boolean register) {
mPendingAppLinkInfo.add(Pair.create(info, register));
}
@@ -1866,6 +1876,16 @@ public class TvIAppManagerService extends SystemService {
ServiceState serviceState = userState.mServiceStateMap.get(mComponent);
serviceState.mService = ITvInteractiveAppService.Stub.asInterface(service);
+ // Register a callback, if we need to.
+ if (serviceState.mCallback == null) {
+ serviceState.mCallback = new ServiceCallback(mComponent, mUserId);
+ try {
+ serviceState.mService.registerCallback(serviceState.mCallback);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in registerCallback", e);
+ }
+ }
+
if (serviceState.mPendingPrepare) {
final long identity = Binder.clearCallingIdentity();
try {
@@ -1880,10 +1900,10 @@ public class TvIAppManagerService extends SystemService {
}
if (!serviceState.mPendingAppLinkInfo.isEmpty()) {
- for (Iterator<Pair<Bundle, Boolean>> it =
+ for (Iterator<Pair<AppLinkInfo, Boolean>> it =
serviceState.mPendingAppLinkInfo.iterator();
it.hasNext(); ) {
- Pair<Bundle, Boolean> appLinkInfoPair = it.next();
+ Pair<AppLinkInfo, Boolean> appLinkInfoPair = it.next();
final long identity = Binder.clearCallingIdentity();
try {
if (appLinkInfoPair.second) {
@@ -1968,14 +1988,14 @@ public class TvIAppManagerService extends SystemService {
}
@Override
- public void onStateChanged(int type, int state) {
+ public void onStateChanged(int type, int state, int error) {
final long identity = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
ServiceState serviceState = getServiceStateLocked(mComponent, mUserId);
String iAppServiceId = serviceState.mIAppServiceId;
UserState userState = getUserStateLocked(mUserId);
- notifyStateChangedLocked(userState, iAppServiceId, type, state);
+ notifyStateChangedLocked(userState, iAppServiceId, type, state, error);
}
} finally {
Binder.restoreCallingIdentity(identity);
@@ -2072,7 +2092,7 @@ public class TvIAppManagerService extends SystemService {
@Override
public void onCommandRequest(
- @TvIAppService.InteractiveAppServiceCommandType String cmdType,
+ @TvInteractiveAppService.InteractiveAppServiceCommandType String cmdType,
Bundle parameters) {
synchronized (mLock) {
if (DEBUG) {
@@ -2210,16 +2230,16 @@ public class TvIAppManagerService extends SystemService {
}
@Override
- public void onSessionStateChanged(int state) {
+ public void onSessionStateChanged(int state, int err) {
synchronized (mLock) {
if (DEBUG) {
- Slogf.d(TAG, "onSessionStateChanged (state=" + state + ")");
+ Slogf.d(TAG, "onSessionStateChanged (state=" + state + ", err=" + err + ")");
}
if (mSessionState.mSession == null || mSessionState.mClient == null) {
return;
}
try {
- mSessionState.mClient.onSessionStateChanged(state, mSessionState.mSeq);
+ mSessionState.mClient.onSessionStateChanged(state, err, mSessionState.mSeq);
} catch (RemoteException e) {
Slogf.e(TAG, "error in onSessionStateChanged", e);
}
diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerService.java b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
index 344270427569..6aa06e8ee068 100644
--- a/services/core/java/com/android/server/uri/UriGrantsManagerService.java
+++ b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
@@ -104,7 +104,8 @@ import java.util.List;
import java.util.Objects;
/** Manages uri grants. */
-public class UriGrantsManagerService extends IUriGrantsManager.Stub {
+public class UriGrantsManagerService extends IUriGrantsManager.Stub implements
+ UriMetricsHelper.PersistentUriGrantsProvider {
private static final boolean DEBUG = false;
private static final String TAG = "UriGrantsManagerService";
// Maximum number of persisted Uri grants a package is allowed
@@ -115,6 +116,7 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
private final H mH;
ActivityManagerInternal mAmInternal;
PackageManagerInternal mPmInternal;
+ UriMetricsHelper mMetricsHelper;
/** File storing persisted {@link #mGrantedUriPermissions}. */
private final AtomicFile mGrantFile;
@@ -168,16 +170,19 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
}
public static final class Lifecycle extends SystemService {
+ private final Context mContext;
private final UriGrantsManagerService mService;
public Lifecycle(Context context) {
super(context);
+ mContext = context;
mService = new UriGrantsManagerService();
}
@Override
public void onStart() {
publishBinderService(Context.URI_GRANTS_SERVICE, mService);
+ mService.mMetricsHelper = new UriMetricsHelper(mContext, mService);
mService.start();
}
@@ -186,6 +191,7 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
if (phase == PHASE_SYSTEM_SERVICES_READY) {
mService.mAmInternal = LocalServices.getService(ActivityManagerInternal.class);
mService.mPmInternal = LocalServices.getService(PackageManagerInternal.class);
+ mService.mMetricsHelper.registerPuller();
}
}
@@ -1298,20 +1304,50 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
return false;
}
+ @Override
+ public ArrayList<UriPermission> providePersistentUriGrants() {
+ final ArrayList<UriPermission> result = new ArrayList<>();
+
+ synchronized (mLock) {
+ final int size = mGrantedUriPermissions.size();
+ for (int i = 0; i < size; i++) {
+ final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.valueAt(i);
+
+ final int permissionsForPackageSize = perms.size();
+ for (int j = 0; j < permissionsForPackageSize; j++) {
+ final UriPermission permission = perms.valueAt(j);
+
+ if (permission.persistedModeFlags != 0) {
+ result.add(permission);
+ }
+ }
+ }
+ }
+
+ return result;
+ }
+
private void writeGrantedUriPermissions() {
if (DEBUG) Slog.v(TAG, "writeGrantedUriPermissions()");
final long startTime = SystemClock.uptimeMillis();
+ int persistentUriPermissionsCount = 0;
+
// Snapshot permissions so we can persist without lock
ArrayList<UriPermission.Snapshot> persist = Lists.newArrayList();
synchronized (mLock) {
final int size = mGrantedUriPermissions.size();
for (int i = 0; i < size; i++) {
final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.valueAt(i);
- for (UriPermission perm : perms.values()) {
- if (perm.persistedModeFlags != 0) {
- persist.add(perm.snapshot());
+
+ final int permissionsForPackageSize = perms.size();
+ for (int j = 0; j < permissionsForPackageSize; j++) {
+ final UriPermission permission = perms.valueAt(j);
+
+ if (permission.persistedModeFlags != 0) {
+ persistentUriPermissionsCount++;
+ persist.add(permission.snapshot());
}
}
}
@@ -1345,6 +1381,8 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
mGrantFile.failWrite(fos);
}
}
+
+ mMetricsHelper.reportPersistentUriFlushed(persistentUriPermissionsCount);
}
final class H extends Handler {
diff --git a/services/core/java/com/android/server/uri/UriMetricsHelper.java b/services/core/java/com/android/server/uri/UriMetricsHelper.java
new file mode 100644
index 000000000000..dbc959928a3b
--- /dev/null
+++ b/services/core/java/com/android/server/uri/UriMetricsHelper.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.uri;
+
+import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
+
+import android.app.StatsManager;
+import android.content.Context;
+import android.util.SparseArray;
+import android.util.StatsEvent;
+
+import com.android.internal.util.FrameworkStatsLog;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+final class UriMetricsHelper {
+
+ private static final StatsManager.PullAtomMetadata DAILY_PULL_METADATA =
+ new StatsManager.PullAtomMetadata.Builder()
+ .setCoolDownMillis(TimeUnit.DAYS.toMillis(1))
+ .build();
+
+
+ private final Context mContext;
+ private final PersistentUriGrantsProvider mPersistentUriGrantsProvider;
+
+ UriMetricsHelper(Context context, PersistentUriGrantsProvider provider) {
+ mContext = context;
+ mPersistentUriGrantsProvider = provider;
+ }
+
+ void registerPuller() {
+ final StatsManager statsManager = mContext.getSystemService(StatsManager.class);
+ statsManager.setPullAtomCallback(
+ FrameworkStatsLog.PERSISTENT_URI_PERMISSIONS_AMOUNT_PER_PACKAGE,
+ DAILY_PULL_METADATA,
+ DIRECT_EXECUTOR,
+ (atomTag, data) -> {
+ reportPersistentUriPermissionsPerPackage(data);
+ return StatsManager.PULL_SUCCESS;
+ });
+ }
+
+ void reportPersistentUriFlushed(int amount) {
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.PERSISTENT_URI_PERMISSIONS_FLUSHED,
+ amount
+ );
+ }
+
+ private void reportPersistentUriPermissionsPerPackage(List<StatsEvent> data) {
+ final ArrayList<UriPermission> persistentUriGrants =
+ mPersistentUriGrantsProvider.providePersistentUriGrants();
+
+ final SparseArray<Integer> perUidCount = new SparseArray<>();
+
+ final int persistentUriGrantsSize = persistentUriGrants.size();
+ for (int i = 0; i < persistentUriGrantsSize; i++) {
+ final UriPermission uriPermission = persistentUriGrants.get(i);
+
+ perUidCount.put(
+ uriPermission.targetUid,
+ perUidCount.get(uriPermission.targetUid, 0) + 1
+ );
+ }
+
+ final int perUidCountSize = perUidCount.size();
+ for (int i = 0; i < perUidCountSize; i++) {
+ final int uid = perUidCount.keyAt(i);
+ final int amount = perUidCount.valueAt(i);
+
+ data.add(
+ FrameworkStatsLog.buildStatsEvent(
+ FrameworkStatsLog.PERSISTENT_URI_PERMISSIONS_AMOUNT_PER_PACKAGE,
+ uid,
+ amount
+ )
+ );
+ }
+ }
+
+ interface PersistentUriGrantsProvider {
+ ArrayList<UriPermission> providePersistentUriGrants();
+ }
+}
diff --git a/services/core/java/com/android/server/vcn/Vcn.java b/services/core/java/com/android/server/vcn/Vcn.java
index e0cc8e182079..f29c40f74353 100644
--- a/services/core/java/com/android/server/vcn/Vcn.java
+++ b/services/core/java/com/android/server/vcn/Vcn.java
@@ -39,10 +39,13 @@ import android.net.vcn.VcnConfig;
import android.net.vcn.VcnGatewayConnectionConfig;
import android.net.vcn.VcnManager.VcnErrorCode;
import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.Message;
import android.os.ParcelUuid;
import android.provider.Settings;
+import android.telephony.TelephonyCallback;
import android.telephony.TelephonyManager;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
@@ -57,6 +60,7 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
@@ -148,6 +152,10 @@ public class Vcn extends Handler {
@NonNull private final VcnContentResolver mContentResolver;
@NonNull private final ContentObserver mMobileDataSettingsObserver;
+ @NonNull
+ private final Map<Integer, VcnUserMobileDataStateListener> mMobileDataStateListeners =
+ new ArrayMap<>();
+
/**
* Map containing all VcnGatewayConnections and their VcnGatewayConnectionConfigs.
*
@@ -221,6 +229,9 @@ public class Vcn extends Handler {
// Update mIsMobileDataEnabled before starting handling of NetworkRequests.
mIsMobileDataEnabled = getMobileDataStatus();
+ // Register mobile data state listeners.
+ updateMobileDataStateListeners();
+
// Register to receive cached and future NetworkRequests
mVcnContext.getVcnNetworkProvider().registerListener(mRequestListener);
}
@@ -348,6 +359,12 @@ public class Vcn extends Handler {
gatewayConnection.teardownAsynchronously();
}
+ // Unregister MobileDataStateListeners
+ for (VcnUserMobileDataStateListener listener : mMobileDataStateListeners.values()) {
+ getTelephonyManager().unregisterTelephonyCallback(listener);
+ }
+ mMobileDataStateListeners.clear();
+
mCurrentStatus = VCN_STATUS_CODE_INACTIVE;
}
@@ -454,11 +471,40 @@ public class Vcn extends Handler {
gatewayConnection.updateSubscriptionSnapshot(mLastSnapshot);
}
+ updateMobileDataStateListeners();
+
// Update the mobile data state after updating the subscription snapshot as a change in
// subIds for a subGroup may affect the mobile data state.
handleMobileDataToggled();
}
+ private void updateMobileDataStateListeners() {
+ final Set<Integer> subIdsInGroup = mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup);
+ final HandlerExecutor executor = new HandlerExecutor(this);
+
+ // Register new callbacks
+ for (int subId : subIdsInGroup) {
+ if (!mMobileDataStateListeners.containsKey(subId)) {
+ final VcnUserMobileDataStateListener listener =
+ new VcnUserMobileDataStateListener();
+
+ getTelephonyManagerForSubid(subId).registerTelephonyCallback(executor, listener);
+ mMobileDataStateListeners.put(subId, listener);
+ }
+ }
+
+ // Unregister old callbacks
+ Iterator<Entry<Integer, VcnUserMobileDataStateListener>> iterator =
+ mMobileDataStateListeners.entrySet().iterator();
+ while (iterator.hasNext()) {
+ final Entry<Integer, VcnUserMobileDataStateListener> entry = iterator.next();
+ if (!subIdsInGroup.contains(entry.getKey())) {
+ getTelephonyManager().unregisterTelephonyCallback(entry.getValue());
+ iterator.remove();
+ }
+ }
+ }
+
private void handleMobileDataToggled() {
final boolean oldMobileDataEnabledStatus = mIsMobileDataEnabled;
mIsMobileDataEnabled = getMobileDataStatus();
@@ -493,11 +539,8 @@ public class Vcn extends Handler {
}
private boolean getMobileDataStatus() {
- final TelephonyManager genericTelMan =
- mVcnContext.getContext().getSystemService(TelephonyManager.class);
-
for (int subId : mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup)) {
- if (genericTelMan.createForSubscriptionId(subId).isDataEnabled()) {
+ if (getTelephonyManagerForSubid(subId).isDataEnabled()) {
return true;
}
}
@@ -517,6 +560,14 @@ public class Vcn extends Handler {
return request.canBeSatisfiedBy(builder.build());
}
+ private TelephonyManager getTelephonyManager() {
+ return mVcnContext.getContext().getSystemService(TelephonyManager.class);
+ }
+
+ private TelephonyManager getTelephonyManagerForSubid(int subid) {
+ return getTelephonyManager().createForSubscriptionId(subid);
+ }
+
private String getLogPrefix() {
return "["
+ LogUtils.getHashedSubscriptionGroup(mSubscriptionGroup)
@@ -670,6 +721,16 @@ public class Vcn extends Handler {
}
}
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ class VcnUserMobileDataStateListener extends TelephonyCallback
+ implements TelephonyCallback.UserMobileDataStateListener {
+
+ @Override
+ public void onUserMobileDataStateChanged(boolean enabled) {
+ sendMessage(obtainMessage(MSG_EVENT_MOBILE_DATA_TOGGLED));
+ }
+ }
+
/** External dependencies used by Vcn, for injection in tests */
@VisibleForTesting(visibility = Visibility.PRIVATE)
public static class Dependencies {
diff --git a/services/core/java/com/android/server/vibrator/ClippingAmplitudeAndFrequencyAdapter.java b/services/core/java/com/android/server/vibrator/ClippingAmplitudeAndFrequencyAdapter.java
index 8189e74f922c..160f4f971e2f 100644
--- a/services/core/java/com/android/server/vibrator/ClippingAmplitudeAndFrequencyAdapter.java
+++ b/services/core/java/com/android/server/vibrator/ClippingAmplitudeAndFrequencyAdapter.java
@@ -26,8 +26,8 @@ import android.util.Range;
import java.util.List;
/**
- * Adapter that clips frequency values to {@link VibratorInfo#getFrequencyRangeHz()} and
- * amplitude values to respective {@link VibratorInfo#getMaxAmplitude}.
+ * Adapter that clips frequency values to the ones specified by the
+ * {@link VibratorInfo.FrequencyProfile}.
*
* <p>Devices with no frequency control will collapse all frequencies to the resonant frequency and
* leave amplitudes unchanged.
@@ -69,20 +69,20 @@ final class ClippingAmplitudeAndFrequencyAdapter
}
private float clampFrequency(VibratorInfo info, float frequencyHz) {
- Range<Float> frequencyRangeHz = info.getFrequencyRangeHz();
+ Range<Float> frequencyRangeHz = info.getFrequencyProfile().getFrequencyRangeHz();
if (frequencyHz == 0 || frequencyRangeHz == null) {
- return info.getResonantFrequency();
+ return info.getResonantFrequencyHz();
}
return frequencyRangeHz.clamp(frequencyHz);
}
private float clampAmplitude(VibratorInfo info, float frequencyHz, float amplitude) {
- Range<Float> frequencyRangeHz = info.getFrequencyRangeHz();
- if (frequencyRangeHz == null) {
- // No frequency range was specified, leave amplitude unchanged, the frequency will be
- // clamped to the device's resonant frequency.
+ VibratorInfo.FrequencyProfile mapping = info.getFrequencyProfile();
+ if (mapping.isEmpty()) {
+ // No frequency mapping was specified so leave amplitude unchanged.
+ // The frequency will be clamped to the device's resonant frequency.
return amplitude;
}
- return MathUtils.min(amplitude, info.getMaxAmplitude(frequencyHz));
+ return MathUtils.min(amplitude, mapping.getMaxAmplitude(frequencyHz));
}
}
diff --git a/services/core/java/com/android/server/vibrator/RampToStepAdapter.java b/services/core/java/com/android/server/vibrator/RampToStepAdapter.java
index c592a7072407..c943bb283fe7 100644
--- a/services/core/java/com/android/server/vibrator/RampToStepAdapter.java
+++ b/services/core/java/com/android/server/vibrator/RampToStepAdapter.java
@@ -94,6 +94,6 @@ final class RampToStepAdapter implements VibrationEffectAdapters.SegmentsAdapter
}
private static float fillEmptyFrequency(VibratorInfo info, float frequencyHz) {
- return frequencyHz == 0 ? info.getResonantFrequency() : frequencyHz;
+ return frequencyHz == 0 ? info.getResonantFrequencyHz() : frequencyHz;
}
}
diff --git a/services/core/java/com/android/server/vibrator/StepToRampAdapter.java b/services/core/java/com/android/server/vibrator/StepToRampAdapter.java
index 5ace3896f387..86fc642f7230 100644
--- a/services/core/java/com/android/server/vibrator/StepToRampAdapter.java
+++ b/services/core/java/com/android/server/vibrator/StepToRampAdapter.java
@@ -148,6 +148,6 @@ final class StepToRampAdapter implements VibrationEffectAdapters.SegmentsAdapter
}
private static float fillEmptyFrequency(VibratorInfo info, float frequencyHz) {
- return frequencyHz == 0 ? info.getResonantFrequency() : frequencyHz;
+ return frequencyHz == 0 ? info.getResonantFrequencyHz() : frequencyHz;
}
}
diff --git a/services/core/java/com/android/server/vibrator/VibrationScaler.java b/services/core/java/com/android/server/vibrator/VibrationScaler.java
index f481772d7a7f..a528f063e875 100644
--- a/services/core/java/com/android/server/vibrator/VibrationScaler.java
+++ b/services/core/java/com/android/server/vibrator/VibrationScaler.java
@@ -74,6 +74,12 @@ final class VibrationScaler {
public int getExternalVibrationScale(int usageHint) {
int defaultIntensity = mSettingsController.getDefaultIntensity(usageHint);
int currentIntensity = mSettingsController.getCurrentIntensity(usageHint);
+
+ if (currentIntensity == Vibrator.VIBRATION_INTENSITY_OFF) {
+ // Bypassing user settings, or it has changed between checking and scaling. Use default.
+ return SCALE_NONE;
+ }
+
int scaleLevel = currentIntensity - defaultIntensity;
if (scaleLevel >= SCALE_VERY_LOW && scaleLevel <= SCALE_VERY_HIGH) {
@@ -97,6 +103,12 @@ final class VibrationScaler {
public <T extends VibrationEffect> T scale(VibrationEffect effect, int usageHint) {
int defaultIntensity = mSettingsController.getDefaultIntensity(usageHint);
int currentIntensity = mSettingsController.getCurrentIntensity(usageHint);
+
+ if (currentIntensity == Vibrator.VIBRATION_INTENSITY_OFF) {
+ // Bypassing user settings, or it has changed between checking and scaling. Use default.
+ currentIntensity = defaultIntensity;
+ }
+
int newEffectStrength = intensityToEffectStrength(currentIntensity);
effect = effect.applyEffectStrength(newEffectStrength).resolve(mDefaultVibrationAmplitude);
ScaleLevel scale = mScaleLevels.get(currentIntensity - defaultIntensity);
@@ -121,6 +133,12 @@ final class VibrationScaler {
*/
public PrebakedSegment scale(PrebakedSegment prebaked, int usageHint) {
int currentIntensity = mSettingsController.getCurrentIntensity(usageHint);
+
+ if (currentIntensity == Vibrator.VIBRATION_INTENSITY_OFF) {
+ // Bypassing user settings, or it has changed between checking and scaling. Use default.
+ currentIntensity = mSettingsController.getDefaultIntensity(usageHint);
+ }
+
int newEffectStrength = intensityToEffectStrength(currentIntensity);
return prebaked.applyEffectStrength(newEffectStrength);
}
diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java
index df6ffa2bd009..eafd9d7f0b6c 100644
--- a/services/core/java/com/android/server/vibrator/VibrationSettings.java
+++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java
@@ -97,6 +97,15 @@ final class VibrationSettings {
USAGE_ALARM,
USAGE_COMMUNICATION_REQUEST));
+ /**
+ * Usage allowed for vibrations when {@link Settings.System#VIBRATE_ON} is disabled.
+ *
+ * <p>The only allowed usage is accessibility, which is applied when the user enables talkback.
+ * Other usages that must ignore this setting should use
+ * {@link VibrationAttributes#FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF}.
+ */
+ private static final int VIBRATE_ON_DISABLED_USAGE_ALLOWED = USAGE_ACCESSIBILITY;
+
/** Listener for changes on vibration settings. */
interface OnVibratorSettingsChanged {
/** Callback triggered when any of the vibrator settings change. */
@@ -127,6 +136,8 @@ final class VibrationSettings {
private SparseIntArray mCurrentVibrationIntensities = new SparseIntArray();
@GuardedBy("mLock")
private boolean mBatterySaverMode;
+ @GuardedBy("mLock")
+ private boolean mVibrateOn;
VibrationSettings(Context context, Handler handler) {
this(context, handler, new VibrationConfig(context.getResources()));
@@ -168,7 +179,7 @@ final class VibrationSettings {
try {
ActivityManager.getService().registerUidObserver(mUidObserver,
ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE,
- ActivityManager.PROCESS_STATE_UNKNOWN, null);
+ ActivityManager.PROCESS_STATE_UNKNOWN, mContext.getOpPackageName());
} catch (RemoteException e) {
// ignored; both services live in system_server
}
@@ -199,6 +210,7 @@ final class VibrationSettings {
// Listen to all settings that might affect the result of Vibrator.getVibrationIntensity.
registerSettingsObserver(Settings.System.getUriFor(Settings.System.VIBRATE_INPUT_DEVICES));
+ registerSettingsObserver(Settings.System.getUriFor(Settings.System.VIBRATE_ON));
registerSettingsObserver(Settings.System.getUriFor(Settings.System.VIBRATE_WHEN_RINGING));
registerSettingsObserver(Settings.System.getUriFor(Settings.System.APPLY_RAMPING_RINGER));
registerSettingsObserver(Settings.System.getUriFor(
@@ -314,9 +326,14 @@ final class VibrationSettings {
return Vibration.Status.IGNORED_FOR_POWER;
}
- int intensity = getCurrentIntensity(usage);
- if (intensity == Vibrator.VIBRATION_INTENSITY_OFF) {
- return Vibration.Status.IGNORED_FOR_SETTINGS;
+ if (!attrs.isFlagSet(VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF)) {
+ if (!mVibrateOn && (VIBRATE_ON_DISABLED_USAGE_ALLOWED != usage)) {
+ return Vibration.Status.IGNORED_FOR_SETTINGS;
+ }
+
+ if (getCurrentIntensity(usage) == Vibrator.VIBRATION_INTENSITY_OFF) {
+ return Vibration.Status.IGNORED_FOR_SETTINGS;
+ }
}
if (!shouldVibrateForRingerModeLocked(usage)) {
@@ -355,6 +372,7 @@ final class VibrationSettings {
void updateSettings() {
synchronized (mLock) {
mVibrateInputDevices = loadSystemSetting(Settings.System.VIBRATE_INPUT_DEVICES, 0) > 0;
+ mVibrateOn = loadSystemSetting(Settings.System.VIBRATE_ON, 1) > 0;
int alarmIntensity = toIntensity(
loadSystemSetting(Settings.System.ALARM_VIBRATION_INTENSITY, -1),
@@ -435,8 +453,9 @@ final class VibrationSettings {
+ "mVibratorConfig=" + mVibrationConfig
+ ", mVibrateInputDevices=" + mVibrateInputDevices
+ ", mBatterySaverMode=" + mBatterySaverMode
- + ", mProcStatesCache=" + mUidObserver.mProcStatesCache
+ + ", mVibrateOn=" + mVibrateOn
+ ", mVibrationIntensities=" + vibrationIntensitiesString
+ + ", mProcStatesCache=" + mUidObserver.mProcStatesCache
+ '}';
}
}
@@ -444,6 +463,8 @@ final class VibrationSettings {
/** Write current settings into given {@link ProtoOutputStream}. */
public void dumpProto(ProtoOutputStream proto) {
synchronized (mLock) {
+ proto.write(VibratorManagerServiceDumpProto.VIBRATE_ON, mVibrateOn);
+ proto.write(VibratorManagerServiceDumpProto.LOW_POWER_MODE, mBatterySaverMode);
proto.write(VibratorManagerServiceDumpProto.ALARM_INTENSITY,
getCurrentIntensity(USAGE_ALARM));
proto.write(VibratorManagerServiceDumpProto.ALARM_DEFAULT_INTENSITY,
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index 27566b301a6e..a95b6c955d63 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -80,6 +80,9 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
private static final boolean DEBUG = false;
private static final VibrationAttributes DEFAULT_ATTRIBUTES =
new VibrationAttributes.Builder().build();
+ private static final int ATTRIBUTES_ALL_BYPASS_FLAGS =
+ VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY
+ | VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF;
/** Lifecycle responsible for initializing this class at the right system server phases. */
public static class Lifecycle extends SystemService {
@@ -975,12 +978,12 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
usage = VibrationAttributes.USAGE_TOUCH;
}
int flags = attrs.getFlags();
- if (attrs.isFlagSet(VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY)) {
+ if ((flags & ATTRIBUTES_ALL_BYPASS_FLAGS) != 0) {
if (!(hasPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
|| hasPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
|| hasPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING))) {
- // Remove bypass policy flag from attributes if the app does not have permissions.
- flags &= ~VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY;
+ // Remove bypass flags from attributes if the app does not have permissions.
+ flags &= ~ATTRIBUTES_ALL_BYPASS_FLAGS;
}
}
if ((usage == attrs.getUsage()) && (flags == attrs.getFlags())) {
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 2ec42b41fc9f..ee2cc7bd7486 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -81,7 +81,9 @@ import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
+import android.os.ResultReceiver;
import android.os.SELinux;
+import android.os.ShellCallback;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
@@ -92,6 +94,7 @@ import android.service.wallpaper.IWallpaperService;
import android.service.wallpaper.WallpaperService;
import android.system.ErrnoException;
import android.system.Os;
+import android.util.ArrayMap;
import android.util.EventLog;
import android.util.Slog;
import android.util.SparseArray;
@@ -420,7 +423,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
Slog.v(TAG, "notifyWallpaperColorsChangedOnDisplay " + which);
}
- needsExtraction = wallpaper.primaryColors == null;
+ needsExtraction = wallpaper.primaryColors == null || wallpaper.mIsColorExtractedFromDim;
}
if (needsExtraction) {
@@ -491,12 +494,17 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
String cropFile = null;
boolean defaultImageWallpaper = false;
int wallpaperId;
+ float dimAmount;
+
+ synchronized (mLock) {
+ wallpaper.mIsColorExtractedFromDim = false;
+ }
if (wallpaper.equals(mFallbackWallpaper)) {
synchronized (mLock) {
if (mFallbackWallpaper.primaryColors != null) return;
}
- final WallpaperColors colors = extractDefaultImageWallpaperColors();
+ final WallpaperColors colors = extractDefaultImageWallpaperColors(wallpaper);
synchronized (mLock) {
mFallbackWallpaper.primaryColors = colors;
}
@@ -513,18 +521,19 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
defaultImageWallpaper = true;
}
wallpaperId = wallpaper.wallpaperId;
+ dimAmount = wallpaper.mWallpaperDimAmount;
}
WallpaperColors colors = null;
if (cropFile != null) {
Bitmap bitmap = BitmapFactory.decodeFile(cropFile);
if (bitmap != null) {
- colors = WallpaperColors.fromBitmap(bitmap);
+ colors = WallpaperColors.fromBitmap(bitmap, dimAmount);
bitmap.recycle();
}
} else if (defaultImageWallpaper) {
// There is no crop and source file because this is default image wallpaper.
- colors = extractDefaultImageWallpaperColors();
+ colors = extractDefaultImageWallpaperColors(wallpaper);
}
if (colors == null) {
@@ -544,11 +553,13 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
}
}
- private WallpaperColors extractDefaultImageWallpaperColors() {
+ private WallpaperColors extractDefaultImageWallpaperColors(WallpaperData wallpaper) {
if (DEBUG) Slog.d(TAG, "Extract default image wallpaper colors");
+ float dimAmount;
synchronized (mLock) {
if (mCacheDefaultImageWallpaperColors != null) return mCacheDefaultImageWallpaperColors;
+ dimAmount = wallpaper.mWallpaperDimAmount;
}
WallpaperColors colors = null;
@@ -561,7 +572,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
final BitmapFactory.Options options = new BitmapFactory.Options();
final Bitmap bitmap = BitmapFactory.decodeStream(is, null, options);
if (bitmap != null) {
- colors = WallpaperColors.fromBitmap(bitmap);
+ colors = WallpaperColors.fromBitmap(bitmap, dimAmount);
bitmap.recycle();
}
} catch (OutOfMemoryError e) {
@@ -948,6 +959,23 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
WallpaperObserver wallpaperObserver;
/**
+ * The dim amount to be applied to the wallpaper.
+ */
+ float mWallpaperDimAmount = 0.0f;
+
+ /**
+ * A map to keep track of the dimming set by different applications. The key is the calling
+ * UID and the value is the dim amount.
+ */
+ ArrayMap<Integer, Float> mUidToDimAmount = new ArrayMap<>();
+
+ /**
+ * Whether we need to extract the wallpaper colors again to calculate the dark hints
+ * after dimming is applied.
+ */
+ boolean mIsColorExtractedFromDim;
+
+ /**
* List of callbacks registered they should each be notified when the wallpaper is changed.
*/
private RemoteCallbackList<IWallpaperManagerCallback> callbacks
@@ -1487,6 +1515,15 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
Slog.w(TAG, "Failed to register local colors areas", e);
}
}
+
+ if (mWallpaper.mWallpaperDimAmount != 0f) {
+ try {
+ connector.mEngine.applyDimming(mWallpaper.mWallpaperDimAmount);
+ notifyWallpaperColorsChanged(mWallpaper, FLAG_SYSTEM);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to dim wallpaper", e);
+ }
+ }
}
}
@@ -2536,6 +2573,98 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
if (purgeAreas.size() > 0) engine.removeLocalColorsAreas(purgeAreas);
}
+ /**
+ * Returns true if the lock screen wallpaper exists (different wallpaper from the system)
+ */
+ @Override
+ public boolean lockScreenWallpaperExists() {
+ synchronized (mLock) {
+ return mLockWallpaperMap.get(mCurrentUserId) != null;
+ }
+ }
+
+ /**
+ * Sets wallpaper dim amount for the calling UID. This only applies to FLAG_SYSTEM wallpaper as
+ * the lock screen does not have a wallpaper component, so we use mWallpaperMap.
+ *
+ * @param dimAmount Dim amount which would be blended with the system default dimming.
+ */
+ @Override
+ public void setWallpaperDimAmount(float dimAmount) throws RemoteException {
+ checkPermission(android.Manifest.permission.SET_WALLPAPER_DIM_AMOUNT);
+ int uid = Binder.getCallingUid();
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ WallpaperData wallpaper = mWallpaperMap.get(mCurrentUserId);
+ WallpaperData lockWallpaper = mLockWallpaperMap.get(mCurrentUserId);
+
+ if (dimAmount == 0.0f) {
+ wallpaper.mUidToDimAmount.remove(uid);
+ } else {
+ wallpaper.mUidToDimAmount.put(uid, dimAmount);
+ }
+
+ float maxDimAmount = getHighestDimAmountFromMap(wallpaper.mUidToDimAmount);
+ wallpaper.mWallpaperDimAmount = maxDimAmount;
+ // Also set the dim amount to the lock screen wallpaper if the lock and home screen
+ // do not share the same wallpaper
+ if (lockWallpaper != null) {
+ lockWallpaper.mWallpaperDimAmount = maxDimAmount;
+ }
+
+ if (wallpaper.connection != null) {
+ wallpaper.connection.forEachDisplayConnector(connector -> {
+ if (connector.mEngine != null) {
+ try {
+ connector.mEngine.applyDimming(maxDimAmount);
+ } catch (RemoteException e) {
+ Slog.w(TAG,
+ "Can't apply dimming on wallpaper display connector", e);
+ }
+ }
+ });
+ // Need to extract colors again to re-calculate dark hints after
+ // applying dimming.
+ wallpaper.mIsColorExtractedFromDim = true;
+ notifyWallpaperColorsChanged(wallpaper, FLAG_SYSTEM);
+ if (lockWallpaper != null) {
+ lockWallpaper.mIsColorExtractedFromDim = true;
+ notifyWallpaperColorsChanged(lockWallpaper, FLAG_LOCK);
+ }
+ saveSettingsLocked(wallpaper.userId);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override
+ public float getWallpaperDimAmount() {
+ checkPermission(android.Manifest.permission.SET_WALLPAPER_DIM_AMOUNT);
+ synchronized (mLock) {
+ WallpaperData data = mWallpaperMap.get(mCurrentUserId);
+ return data.mWallpaperDimAmount;
+ }
+ }
+
+ /**
+ * Gets the highest dim amount among all the calling UIDs that set the wallpaper dim amount.
+ * Return 0f as default value to indicate no application has dimmed the wallpaper.
+ *
+ * @param uidToDimAmountMap Map of UIDs to dim amounts
+ */
+ private float getHighestDimAmountFromMap(ArrayMap<Integer, Float> uidToDimAmountMap) {
+ float maxDimAmount = 0.0f;
+ for (Map.Entry<Integer, Float> entry : uidToDimAmountMap.entrySet()) {
+ if (entry.getValue() > maxDimAmount) {
+ maxDimAmount = entry.getValue();
+ }
+ }
+ return maxDimAmount;
+ }
+
@Override
public WallpaperColors getWallpaperColors(int which, int userId, int displayId)
throws RemoteException {
@@ -2562,7 +2691,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
if (wallpaperData == null) {
return null;
}
- shouldExtract = wallpaperData.primaryColors == null;
+ shouldExtract = wallpaperData.primaryColors == null
+ || wallpaperData.mIsColorExtractedFromDim;
}
if (shouldExtract) {
@@ -2664,6 +2794,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
lockWP.cropHint.set(sysWP.cropHint);
lockWP.allowBackup = sysWP.allowBackup;
lockWP.primaryColors = sysWP.primaryColors;
+ lockWP.mWallpaperDimAmount = sysWP.mWallpaperDimAmount;
// Migrate the bitmap files outright; no need to copy
try {
@@ -3191,6 +3322,18 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
out.attributeInt(null, "paddingBottom", wpdData.mPadding.bottom);
}
+ out.attributeFloat(null, "dimAmount", wallpaper.mWallpaperDimAmount);
+ int dimAmountsCount = wallpaper.mUidToDimAmount.size();
+ out.attributeInt(null, "dimAmountsCount", dimAmountsCount);
+ if (dimAmountsCount > 0) {
+ int index = 0;
+ for (Map.Entry<Integer, Float> entry : wallpaper.mUidToDimAmount.entrySet()) {
+ out.attributeInt(null, "dimUID" + index, entry.getKey());
+ out.attributeFloat(null, "dimValue" + index, entry.getValue());
+ index++;
+ }
+ }
+
if (wallpaper.primaryColors != null) {
int colorsCount = wallpaper.primaryColors.getMainColors().size();
out.attributeInt(null, "colorsCount", colorsCount);
@@ -3267,6 +3410,10 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
return parser.getAttributeInt(null, name, defValue);
}
+ private float getAttributeFloat(TypedXmlPullParser parser, String name, float defValue) {
+ return parser.getAttributeFloat(null, name, defValue);
+ }
+
/**
* Sometimes it is expected the wallpaper map may not have a user's data. E.g. This could
* happen during user switch. The async user switch observer may not have received
@@ -3471,6 +3618,17 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
wpData.mPadding.top = getAttributeInt(parser, "paddingTop", 0);
wpData.mPadding.right = getAttributeInt(parser, "paddingRight", 0);
wpData.mPadding.bottom = getAttributeInt(parser, "paddingBottom", 0);
+ wallpaper.mWallpaperDimAmount = getAttributeFloat(parser, "dimAmount", 0f);
+ int dimAmountsCount = getAttributeInt(parser, "dimAmountsCount", 0);
+ if (dimAmountsCount > 0) {
+ ArrayMap<Integer, Float> allDimAmounts = new ArrayMap<>(dimAmountsCount);
+ for (int i = 0; i < dimAmountsCount; i++) {
+ int uid = getAttributeInt(parser, "dimUID" + i, 0);
+ float dimValue = getAttributeFloat(parser, "dimValue" + i, 0f);
+ allDimAmounts.put(uid, dimValue);
+ }
+ wallpaper.mUidToDimAmount = allDimAmounts;
+ }
int colorsCount = getAttributeInt(parser, "colorsCount", 0);
int allColorsCount = getAttributeInt(parser, "allColorsCount", 0);
if (allColorsCount > 0) {
@@ -3637,6 +3795,14 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
return false;
}
+ @Override // Binder call
+ public void onShellCommand(FileDescriptor in, FileDescriptor out,
+ FileDescriptor err, String[] args, ShellCallback callback,
+ ResultReceiver resultReceiver) {
+ new WallpaperManagerShellCommand(WallpaperManagerService.this).exec(this, in, out, err,
+ args, callback, resultReceiver);
+ }
+
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
@@ -3664,6 +3830,13 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
pw.print(" mName="); pw.println(wallpaper.name);
pw.print(" mAllowBackup="); pw.println(wallpaper.allowBackup);
pw.print(" mWallpaperComponent="); pw.println(wallpaper.wallpaperComponent);
+ pw.print(" mWallpaperDimAmount="); pw.println(wallpaper.mWallpaperDimAmount);
+ pw.print(" isColorExtracted="); pw.println(wallpaper.mIsColorExtractedFromDim);
+ pw.println(" mUidToDimAmount:");
+ for (Map.Entry<Integer, Float> entry : wallpaper.mUidToDimAmount.entrySet()) {
+ pw.print(" UID="); pw.print(entry.getKey());
+ pw.print(" dimAmount="); pw.println(entry.getValue());
+ }
if (wallpaper.connection != null) {
WallpaperConnection conn = wallpaper.connection;
pw.print(" Wallpaper connection ");
@@ -3695,6 +3868,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
pw.print(" mCropHint="); pw.println(wallpaper.cropHint);
pw.print(" mName="); pw.println(wallpaper.name);
pw.print(" mAllowBackup="); pw.println(wallpaper.allowBackup);
+ pw.print(" mWallpaperDimAmount="); pw.println(wallpaper.mWallpaperDimAmount);
}
pw.println("Fallback wallpaper state:");
pw.print(" User "); pw.print(mFallbackWallpaper.userId);
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerShellCommand.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerShellCommand.java
new file mode 100644
index 000000000000..fc827b40f3a1
--- /dev/null
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerShellCommand.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.wallpaper;
+
+import android.os.RemoteException;
+import android.os.ShellCommand;
+import android.util.Log;
+
+import java.io.PrintWriter;
+
+/**
+ * Shell Command class to run adb commands on the wallpaper service
+ */
+public class WallpaperManagerShellCommand extends ShellCommand {
+ private static final String TAG = "WallpaperManagerShellCommand";
+
+ private final WallpaperManagerService mService;
+
+ public WallpaperManagerShellCommand(WallpaperManagerService service) {
+ mService = service;
+ }
+
+ @Override
+ public int onCommand(String cmd) {
+ if (cmd == null) {
+ onHelp();
+ return 1;
+ }
+ switch(cmd) {
+ case "set-dim-amount":
+ return setWallpaperDimAmount();
+ case "get-dim-amount":
+ return getWallpaperDimAmount();
+ case "-h":
+ case "help":
+ onHelp();
+ return 0;
+ default:
+ return handleDefaultCommands(cmd);
+ }
+ }
+
+ @Override
+ public void onHelp() {
+ final PrintWriter pw = getOutPrintWriter();
+ pw.println("Wallpaper manager commands:");
+ pw.println(" help");
+ pw.println(" Print this help text.");
+ pw.println();
+ pw.println(" set-dim-amount DIMMING");
+ pw.println(" Sets the current dimming value to DIMMING (a number between 0 and 1).");
+ pw.println();
+ pw.println(" get-dim-amount");
+ pw.println(" Get the current wallpaper dim amount.");
+ }
+
+ /**
+ * Sets the wallpaper dim amount between [0f, 1f] which would be blended with the system default
+ * dimming. 0f doesn't add any additional dimming and 1f makes the wallpaper fully black.
+ */
+ private int setWallpaperDimAmount() {
+ float dimAmount = Float.parseFloat(getNextArgRequired());
+ try {
+ mService.setWallpaperDimAmount(dimAmount);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Can't set wallpaper dim amount");
+ }
+ getOutPrintWriter().println("Dimming the wallpaper to: " + dimAmount);
+ return 0;
+ }
+
+ /**
+ * Gets the current additional dim amount set on the wallpaper. 0f means no application has
+ * added any dimming on top of the system default dim amount.
+ */
+ private int getWallpaperDimAmount() {
+ float dimAmount = mService.getWallpaperDimAmount();
+ getOutPrintWriter().println("The current wallpaper dim amount is: " + dimAmount);
+ return 0;
+ }
+}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index c2765db4ccff..76434c71d342 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2764,9 +2764,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
boolean isResizeable() {
+ return isResizeable(/* checkPictureInPictureSupport */ true);
+ }
+
+ boolean isResizeable(boolean checkPictureInPictureSupport) {
return mAtmService.mForceResizableActivities
|| ActivityInfo.isResizeableMode(info.resizeMode)
- || info.supportsPictureInPicture()
+ || (info.supportsPictureInPicture() && checkPictureInPictureSupport)
// If the activity can be embedded, it should inherit the bounds of task fragment.
|| isEmbedded();
}
@@ -7360,12 +7364,17 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
@VisibleForTesting
void clearSizeCompatMode() {
+ final float lastSizeCompatScale = mSizeCompatScale;
mInSizeCompatModeForBounds = false;
mSizeCompatScale = 1f;
mSizeCompatBounds = null;
mCompatDisplayInsets = null;
+ if (mSizeCompatScale != lastSizeCompatScale) {
+ forAllWindows(WindowState::updateGlobalScale, false /* traverseTopToBottom */);
+ }
- onRequestedOverrideConfigurationChanged(getRequestedOverrideConfiguration());
+ // Clear config override in #updateCompatDisplayInsets().
+ onRequestedOverrideConfigurationChanged(EMPTY);
}
@Override
@@ -7680,10 +7689,16 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// orientation with insets applied.
return;
}
- // Activity should be resizable if the task is.
+ // Not using Task#isResizeable() or ActivityRecord#isResizeable() directly because app
+ // compatibility testing showed that android:supportsPictureInPicture="true" alone is not
+ // sufficient signal for not letterboxing an app.
+ // TODO(214602463): Remove multi-window check since orientation and aspect ratio
+ // restrictions should always be applied in multi-window.
final boolean isResizeable = task != null
- ? task.isResizeable() || isResizeable()
- : isResizeable();
+ // Activity should be resizable if the task is.
+ ? task.isResizeable(/* checkPictureInPictureSupport */ false)
+ || isResizeable(/* checkPictureInPictureSupport */ false)
+ : isResizeable(/* checkPictureInPictureSupport */ false);
if (WindowConfiguration.inMultiWindowMode(windowingMode) && isResizeable) {
// Ignore orientation request for resizable apps in multi window.
return;
@@ -7914,6 +7929,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
final int contentH = resolvedAppBounds.height();
final int viewportW = containerAppBounds.width();
final int viewportH = containerAppBounds.height();
+ final float lastSizeCompatScale = mSizeCompatScale;
// Only allow to scale down.
mSizeCompatScale = (contentW <= viewportW && contentH <= viewportH)
? 1f : Math.min((float) viewportW / contentW, (float) viewportH / contentH);
@@ -7932,6 +7948,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
} else {
mSizeCompatBounds = null;
}
+ if (mSizeCompatScale != lastSizeCompatScale) {
+ forAllWindows(WindowState::updateGlobalScale, false /* traverseTopToBottom */);
+ }
// Vertically center within parent (bounds) - this is a UX choice and exclude the horizontal
// decor if needed. Horizontal position is adjusted in
@@ -8199,8 +8218,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
final float maxAspectRatio = info.getMaxAspectRatio();
final Task rootTask = getRootTask();
final float minAspectRatio = getMinAspectRatio();
+ // Not using ActivityRecord#isResizeable() directly because app compatibility testing
+ // showed that android:supportsPictureInPicture="true" alone is not sufficient signal for
+ // not letterboxing an app.
+ // TODO(214602463): Remove multi-window check since orientation and aspect ratio
+ // restrictions should always be applied in multi-window.
if (task == null || rootTask == null
- || (inMultiWindowMode() && !shouldCreateCompatDisplayInsets()
+ || (inMultiWindowMode() && isResizeable(/* checkPictureInPictureSupport */ false)
&& !fixedOrientationLetterboxed)
|| (maxAspectRatio < 1 && minAspectRatio < 1 && desiredAspectRatio < 1)
|| isInVrUiMode(getConfiguration())) {
diff --git a/services/core/java/com/android/server/wm/ActivityRecordInputSink.java b/services/core/java/com/android/server/wm/ActivityRecordInputSink.java
index 4b3078ae7c00..316bf2017585 100644
--- a/services/core/java/com/android/server/wm/ActivityRecordInputSink.java
+++ b/services/core/java/com/android/server/wm/ActivityRecordInputSink.java
@@ -22,7 +22,6 @@ import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import android.app.compat.CompatChanges;
import android.compat.annotation.ChangeId;
-import android.compat.annotation.Overridable;
import android.os.IBinder;
import android.os.InputConstants;
import android.os.Looper;
@@ -47,7 +46,6 @@ class ActivityRecordInputSink {
* Feature flag for making Activities consume all touches within their task bounds.
*/
@ChangeId
- @Overridable
static final long ENABLE_TOUCH_OPAQUE_ACTIVITIES = 194480991L;
private static final String TAG = "ActivityRecordInputSink";
@@ -116,7 +114,7 @@ class ActivityRecordInputSink {
private InputWindowHandle createInputWindowHandle() {
InputWindowHandle inputWindowHandle = new InputWindowHandle(null,
mActivityRecord.getDisplayId());
- inputWindowHandle.replaceTouchableRegionWithCrop(mActivityRecord.getSurfaceControl());
+ inputWindowHandle.replaceTouchableRegionWithCrop = true;
inputWindowHandle.name = mName;
inputWindowHandle.ownerUid = Process.myUid();
inputWindowHandle.ownerPid = Process.myPid();
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index ddd624d115c3..ed9dcef864c6 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -221,10 +221,10 @@ import android.util.SparseArray;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
import android.view.IRecentsAnimationRunner;
-import android.view.IRemoteAnimationRunner;
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationDefinition;
import android.view.WindowManager;
+import android.window.BackNavigationInfo;
import android.window.IWindowOrganizerController;
import android.window.SplashScreenView.SplashScreenViewParcelable;
import android.window.TaskSnapshot;
@@ -458,7 +458,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
private final ClientLifecycleManager mLifecycleManager;
@Nullable
- private final BackGestureController mBackGestureController;
+ private final BackNavigationController mBackNavigationController;
private TaskChangeNotificationController mTaskChangeNotificationController;
/** The controller for all operations related to locktask. */
@@ -836,8 +836,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
mSystemThread = ActivityThread.currentActivityThread();
mUiContext = mSystemThread.getSystemUiContext();
mLifecycleManager = new ClientLifecycleManager();
- mBackGestureController = BackGestureController.isEnabled() ? new BackGestureController()
- : null;
mVisibleActivityProcessTracker = new VisibleActivityProcessTracker(this);
mInternal = new LocalService();
GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version", GL_ES_VERSION_UNDEFINED);
@@ -845,6 +843,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
mTaskOrganizerController = mWindowOrganizerController.mTaskOrganizerController;
mTaskFragmentOrganizerController =
mWindowOrganizerController.mTaskFragmentOrganizerController;
+ mBackNavigationController = BackNavigationController.isEnabled()
+ ? new BackNavigationController() : null;
}
public void onSystemReady() {
@@ -1022,6 +1022,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
mLockTaskController.setWindowManager(wm);
mTaskSupervisor.setWindowManager(wm);
mRootWindowContainer.setWindowManager(wm);
+ if (mBackNavigationController != null) {
+ mBackNavigationController.setTaskSnapshotController(wm.mTaskSnapshotController);
+ }
}
}
@@ -1768,11 +1771,13 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
@Override
- public void startBackPreview(IRemoteAnimationRunner runner) {
- if (mBackGestureController == null) {
- return;
+ public BackNavigationInfo startBackNavigation() {
+ mAmInternal.enforceCallingPermission(START_TASKS_FROM_RECENTS,
+ "startBackNavigation()");
+ if (mBackNavigationController == null) {
+ return null;
}
- mBackGestureController.startBackPreview();
+ return mBackNavigationController.startBackNavigation(getTopDisplayFocusedRootTask());
}
/**
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
new file mode 100644
index 000000000000..a8779fa79675
--- /dev/null
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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_BACK_PREVIEW;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.WindowConfiguration;
+import android.content.ComponentName;
+import android.hardware.HardwareBuffer;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+import android.os.SystemProperties;
+import android.util.Slog;
+import android.view.SurfaceControl;
+import android.window.BackNavigationInfo;
+import android.window.TaskSnapshot;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.protolog.common.ProtoLog;
+
+/**
+ * Controller to handle actions related to the back gesture on the server side.
+ */
+class BackNavigationController {
+
+ private static final String TAG = "BackNavigationController";
+ private static final String BACK_PREDICTABILITY_PROP = "persist.debug.back_predictability";
+
+ @Nullable
+ private TaskSnapshotController mTaskSnapshotController;
+
+ /**
+ * Returns true if the back predictability feature is enabled
+ */
+ static boolean isEnabled() {
+ return SystemProperties.getInt(BACK_PREDICTABILITY_PROP, 0) > 0;
+ }
+
+ /**
+ * Set up the necessary leashes and build a {@link BackNavigationInfo} instance for an upcoming
+ * back gesture animation.
+ *
+ * @param task the currently focused {@link Task}.
+ * @return a {@link BackNavigationInfo} instance containing the required leashes and metadata
+ * for the animation.
+ */
+ @Nullable
+ BackNavigationInfo startBackNavigation(@NonNull Task task) {
+ return startBackNavigation(task, null);
+ }
+
+ /**
+ * @param tx, a transaction to be used for the attaching the animation leash.
+ * This is used in tests. If null, the object will be initialized with a new {@link
+ * android.view.SurfaceControl.Transaction}
+ * @see #startBackNavigation(Task)
+ */
+ @VisibleForTesting
+ @Nullable
+ BackNavigationInfo startBackNavigation(@NonNull Task task,
+ @Nullable SurfaceControl.Transaction tx) {
+
+ if (tx == null) {
+ tx = new SurfaceControl.Transaction();
+ }
+
+ int backType = BackNavigationInfo.TYPE_UNDEFINED;
+ Task prevTask = task;
+ ActivityRecord prev;
+ WindowContainer<?> removedWindowContainer;
+ ActivityRecord activityRecord;
+ SurfaceControl animationLeashParent;
+ WindowConfiguration taskWindowConfiguration;
+ SurfaceControl animLeash;
+ HardwareBuffer screenshotBuffer = null;
+ int prevTaskId;
+ int prevUserId;
+
+ synchronized (task.mWmService.mGlobalLock) {
+ activityRecord = task.topRunningActivity();
+ removedWindowContainer = activityRecord;
+ taskWindowConfiguration = task.getTaskInfo().configuration.windowConfiguration;
+
+ ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "startBackNavigation task=%s, topRunningActivity=%s",
+ task, activityRecord);
+
+ // IME is visible, back gesture will dismiss it, nothing to preview.
+ if (task.getDisplayContent().getImeContainer().isVisible()) {
+ return null;
+ }
+
+ // Current Activity is home, there is no previous activity to display
+ if (activityRecord.isActivityTypeHome()) {
+ return null;
+ }
+
+ prev = task.getActivity(
+ (r) -> !r.finishing && r.getTask() == task && !r.isTopRunningActivity());
+
+ if (prev != null) {
+ backType = BackNavigationInfo.TYPE_CROSS_ACTIVITY;
+ } else if (task.returnsToHomeRootTask()) {
+ prevTask = null;
+ removedWindowContainer = task;
+ backType = BackNavigationInfo.TYPE_RETURN_TO_HOME;
+ } else if (activityRecord.isRootOfTask()) {
+ // TODO(208789724): Create single source of truth for this, maybe in
+ // RootWindowContainer
+ // TODO: Also check Task.shouldUpRecreateTaskLocked() for prev logic
+ prevTask = task.mRootWindowContainer.getTaskBelow(task);
+ removedWindowContainer = task;
+ if (prevTask.isActivityTypeHome()) {
+ backType = BackNavigationInfo.TYPE_RETURN_TO_HOME;
+ } else {
+ prev = prevTask.getTopNonFinishingActivity();
+ backType = BackNavigationInfo.TYPE_CROSS_TASK;
+ }
+ }
+
+ prevTaskId = prevTask != null ? prevTask.mTaskId : 0;
+ prevUserId = prevTask != null ? prevTask.mUserId : 0;
+
+ ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "Previous Activity is %s",
+ prev != null ? prev.mActivityComponent : null);
+
+ //TODO(207481538) Remove once the infrastructure to support per-activity screenshot is
+ // implemented. For now we simply have the mBackScreenshots hash map that dumbly
+ // saves the screenshots.
+ if (needsScreenshot(backType) && prev != null && prev.mActivityComponent != null) {
+ screenshotBuffer = getActivitySnapshot(task, prev.mActivityComponent);
+ }
+
+ // Prepare a leash to animate the current top window
+ animLeash = removedWindowContainer.makeAnimationLeash()
+ .setName("BackPreview Leash")
+ .setHidden(false)
+ .setBLASTLayer()
+ .build();
+ removedWindowContainer.reparentSurfaceControl(tx, animLeash);
+
+ animationLeashParent = removedWindowContainer.getAnimationLeashParent();
+ }
+
+ SurfaceControl.Builder builder = new SurfaceControl.Builder()
+ .setName("BackPreview Screenshot")
+ .setParent(animationLeashParent)
+ .setHidden(false)
+ .setBLASTLayer();
+ SurfaceControl screenshotSurface = builder.build();
+
+ // Find a screenshot of the previous activity
+
+ if (needsScreenshot(backType) && prevTask != null) {
+ if (screenshotBuffer == null) {
+ screenshotBuffer = getTaskSnapshot(prevTaskId, prevUserId);
+ }
+ }
+ tx.apply();
+
+ WindowContainer<?> finalRemovedWindowContainer = removedWindowContainer;
+ try {
+ activityRecord.token.linkToDeath(
+ () -> resetSurfaces(finalRemovedWindowContainer), 0);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to link to death", e);
+ resetSurfaces(removedWindowContainer);
+ return null;
+ }
+
+ return new BackNavigationInfo(backType,
+ animLeash,
+ screenshotSurface,
+ screenshotBuffer,
+ taskWindowConfiguration,
+ new RemoteCallback(result -> resetSurfaces(finalRemovedWindowContainer
+ )));
+ }
+
+
+ private HardwareBuffer getActivitySnapshot(@NonNull Task task,
+ ComponentName activityComponent) {
+ // Check if we have a screenshot of the previous activity, indexed by its
+ // component name.
+ SurfaceControl.ScreenshotHardwareBuffer backBuffer = task.mBackScreenshots
+ .get(activityComponent.flattenToString());
+ return backBuffer != null ? backBuffer.getHardwareBuffer() : null;
+
+ }
+
+ private HardwareBuffer getTaskSnapshot(int taskId, int userId) {
+ if (mTaskSnapshotController == null) {
+ return null;
+ }
+ TaskSnapshot snapshot = mTaskSnapshotController.getSnapshot(taskId,
+ userId, true /* restoreFromDisk */, false /* isLowResolution */);
+ return snapshot != null ? snapshot.getHardwareBuffer() : null;
+ }
+
+ private boolean needsScreenshot(int backType) {
+ switch (backType) {
+ case BackNavigationInfo.TYPE_RETURN_TO_HOME:
+ case BackNavigationInfo.TYPE_DIALOG_CLOSE:
+ return false;
+ }
+ return true;
+ }
+
+ private void resetSurfaces(@NonNull WindowContainer<?> windowContainer) {
+ synchronized (windowContainer.mWmService.mGlobalLock) {
+ ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "Back: Reset surfaces");
+ SurfaceControl.Transaction tx = windowContainer.getSyncTransaction();
+ SurfaceControl surfaceControl = windowContainer.getSurfaceControl();
+ if (surfaceControl != null) {
+ tx.reparent(surfaceControl,
+ windowContainer.getParent().getSurfaceControl());
+ tx.apply();
+ }
+ }
+ }
+
+ void setTaskSnapshotController(@Nullable TaskSnapshotController taskSnapshotController) {
+ mTaskSnapshotController = taskSnapshotController;
+ }
+}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index c65ca0847563..e449dde15c67 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -122,6 +122,7 @@ import static com.android.server.wm.DisplayContentProto.INPUT_METHOD_INPUT_TARGE
import static com.android.server.wm.DisplayContentProto.INPUT_METHOD_TARGET;
import static com.android.server.wm.DisplayContentProto.INSETS_SOURCE_PROVIDERS;
import static com.android.server.wm.DisplayContentProto.IS_SLEEPING;
+import static com.android.server.wm.DisplayContentProto.KEEP_CLEAR_AREAS;
import static com.android.server.wm.DisplayContentProto.OPENING_APPS;
import static com.android.server.wm.DisplayContentProto.RESUMED_ACTIVITY;
import static com.android.server.wm.DisplayContentProto.ROOT_DISPLAY_AREA;
@@ -169,6 +170,7 @@ import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.ColorSpace;
import android.graphics.Insets;
+import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
@@ -838,7 +840,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
w.mSurfacePlacementNeeded = true;
w.mLayoutNeeded = false;
- w.prelayout();
final boolean firstLayout = !w.isLaidOut();
getDisplayPolicy().layoutWindowLw(w, null, mDisplayFrames);
w.mLayoutSeq = mLayoutSeq;
@@ -881,7 +882,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
w.mSurfacePlacementNeeded = true;
w.mLayoutNeeded = false;
- w.prelayout();
getDisplayPolicy().layoutWindowLw(w, w.getParentWindow(), mDisplayFrames);
w.mLayoutSeq = mLayoutSeq;
if (DEBUG_LAYOUT) Slog.v(TAG, " LAYOUT: mFrame=" + w.getFrame()
@@ -2001,6 +2001,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
void configureDisplayPolicy() {
+ mRootWindowContainer.updateDisplayImePolicyCache();
mDisplayPolicy.updateConfigurationAndScreenSizeDependentBehaviors();
mDisplayRotation.configure(mBaseDisplayWidth, mBaseDisplayHeight);
}
@@ -2718,6 +2719,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
void onDisplayChanged(DisplayContent dc) {
super.onDisplayChanged(dc);
updateSystemGestureExclusionLimit();
+ updateKeepClearAreas();
}
void updateSystemGestureExclusionLimit() {
@@ -3327,6 +3329,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
}
proto.write(IME_POLICY, getImePolicy());
+ for (Rect r : getKeepClearAreas()) {
+ r.dumpDebug(proto, KEEP_CLEAR_AREAS);
+ }
proto.end(token);
}
@@ -3386,6 +3391,13 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
pw.println(mSystemGestureExclusion);
}
+ final List<Rect> keepClearAreas = getKeepClearAreas();
+ if (!keepClearAreas.isEmpty()) {
+ pw.println();
+ pw.print(" keepClearAreas=");
+ pw.println(keepClearAreas);
+ }
+
pw.println();
pw.println(prefix + "Display areas in top down Z order:");
dumpChildDisplayArea(pw, subPrefix, dumpAll);
@@ -3613,6 +3625,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
adjustForImeIfNeeded();
+ updateKeepClearAreas();
// We may need to schedule some toast windows to be removed. The toasts for an app that
// does not have input focus are removed within a timeout to prevent apps to redress
@@ -3939,6 +3952,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
}
+ // IMPORTANT: When introducing new dependencies in this method, make sure that
+ // changes to those result in RootWindowContainer.updateDisplayImePolicyCache()
+ // being called.
@DisplayImePolicy int getImePolicy() {
if (!isTrusted()) {
return DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
@@ -3987,11 +4003,12 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
if (target == mImeLayeringTarget) {
return;
}
- // Prepare the IME screenshot for the last IME target when its task is applying app
- // transition. This is for the better IME transition to keep IME visibility when
- // transitioning to the next task.
+ // If the IME target is the input target, before it changes, prepare the IME screenshot
+ // for the last IME target when its task is applying app transition. This is for the
+ // better IME transition to keep IME visibility when transitioning to the next task.
if (mImeLayeringTarget != null && mImeLayeringTarget.isAnimating(PARENTS | TRANSITION,
- ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_RECENTS)) {
+ ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_RECENTS)
+ && mImeLayeringTarget == mImeInputTarget) {
attachAndShowImeScreenshotOnTarget();
}
@@ -5477,19 +5494,28 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
mSystemGestureExclusionListeners.unregister(listener);
}
+ void updateKeepClearAreas() {
+ mWmService.mDisplayNotificationController.dispatchKeepClearAreasChanged(
+ this, getKeepClearAreas());
+ }
+
/**
- * @see IWindowManager#setForwardedInsets
+ * Returns all keep-clear areas from visible windows on this display.
*/
- public void setForwardedInsets(Insets insets) {
- if (insets == null) {
- insets = Insets.NONE;
- }
- if (mDisplayPolicy.getForwardedInsets().equals(insets)) {
- return;
- }
- mDisplayPolicy.setForwardedInsets(insets);
- setLayoutNeeded();
- mWmService.mWindowPlacerLocked.requestTraversal();
+ ArrayList<Rect> getKeepClearAreas() {
+ final ArrayList<Rect> keepClearAreas = new ArrayList<Rect>();
+ final Matrix tmpMatrix = new Matrix();
+ final float[] tmpFloat9 = new float[9];
+ forAllWindows(w -> {
+ if (w.isVisible() && !w.inPinnedWindowingMode()) {
+ keepClearAreas.addAll(w.getKeepClearAreas(tmpMatrix, tmpFloat9));
+ }
+
+ // We stop traversing when we reach the base of a fullscreen app.
+ return w.getWindowType() == TYPE_BASE_APPLICATION
+ && w.getWindowingMode() == WINDOWING_MODE_FULLSCREEN;
+ }, true);
+ return keepClearAreas;
}
protected MetricsLogger getMetricsLogger() {
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 0745b3b6d971..18885541b1fe 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -305,10 +305,10 @@ public class DisplayPolicy {
private WindowState mRoundedCornerWindow;
/**
- * Windows to determine the color of status bar. See {@link #mNavBarColorWindowCandidate} for
- * the conditions of being candidate window.
+ * A collection of {@link AppearanceRegion} to indicate that which region of status bar applies
+ * which appearance.
*/
- private final ArrayList<WindowState> mStatusBarColorWindows = new ArrayList<>();
+ private final ArrayList<AppearanceRegion> mStatusBarAppearanceRegionList = new ArrayList<>();
/**
* Windows to determine opacity and background of translucent status bar. The window needs to be
@@ -323,7 +323,7 @@ public class DisplayPolicy {
private InsetsVisibilities mRequestedVisibilities = new InsetsVisibilities();
private AppearanceRegion[] mLastStatusBarAppearanceRegions;
- /** The union of checked bounds while fetching {@link #mStatusBarColorWindows}. */
+ /** The union of checked bounds while building {@link #mStatusBarAppearanceRegionList}. */
private final Rect mStatusBarColorCheckedBounds = new Rect();
/** The union of checked bounds while fetching {@link #mStatusBarBackgroundWindows}. */
@@ -336,6 +336,7 @@ public class DisplayPolicy {
private long mPendingPanicGestureUptime;
private static final Rect sTmpRect = new Rect();
+ private static final Rect sTmpRect2 = new Rect();
private static final Rect sTmpLastParentFrame = new Rect();
private static final Rect sTmpDisplayCutoutSafe = new Rect();
private static final Rect sTmpDisplayFrame = new Rect();
@@ -359,16 +360,6 @@ public class DisplayPolicy {
private int mDisplayCutoutTouchableRegionSize;
- /**
- * The area covered by system windows which belong to another display. Forwarded insets is set
- * in case this is a virtual display, this is displayed on another display that has insets, and
- * the bounds of this display is overlapping with the insets of the host display (e.g. IME is
- * displayed on the host display, and it covers a part of this virtual display.)
- * The forwarded insets is used to compute display frames of this virtual display, which will
- * be then used to layout windows in the virtual display.
- */
- @NonNull private Insets mForwardedInsets = Insets.NONE;
-
private RefreshRatePolicy mRefreshRatePolicy;
/**
@@ -1442,33 +1433,6 @@ public class DisplayPolicy {
return mForceShowSystemBars;
}
- // TODO: Should probably be moved into DisplayFrames.
- /**
- * Return the layout hints for a newly added window. These values are computed on the
- * most recent layout, so they are not guaranteed to be correct.
- *
- * @param attrs The LayoutParams of the window.
- * @param windowToken The token of the window.
- * @param outInsetsState The insets state of this display from the client's perspective.
- * @param localClient Whether the client is from the our process.
- * @return Whether to always consume the system bars.
- * See {@link #areSystemBarsForcedShownLw()}.
- */
- boolean getLayoutHint(LayoutParams attrs, WindowToken windowToken, InsetsState outInsetsState,
- boolean localClient) {
- final InsetsState state =
- mDisplayContent.getInsetsPolicy().getInsetsForWindowMetrics(attrs);
- final boolean hasCompatScale = WindowState.hasCompatScale(attrs, windowToken);
- outInsetsState.set(state, hasCompatScale || localClient);
- if (hasCompatScale) {
- final float compatScale = windowToken != null
- ? windowToken.getSizeCompatScale()
- : mDisplayContent.mCompatibleScreenScale;
- outInsetsState.scale(1f / compatScale);
- }
- return mForceShowSystemBars;
- }
-
/**
* Computes the frames of display (its logical size, rotation and cutout should already be set)
* used to layout window. This method only changes the given display frames, insets state and
@@ -1563,7 +1527,7 @@ public class DisplayPolicy {
mTopFullscreenOpaqueWindowState = null;
mNavBarColorWindowCandidate = null;
mNavBarBackgroundWindow = null;
- mStatusBarColorWindows.clear();
+ mStatusBarAppearanceRegionList.clear();
mStatusBarBackgroundWindows.clear();
mStatusBarColorCheckedBounds.setEmpty();
mStatusBarBackgroundCheckedBounds.setEmpty();
@@ -1643,7 +1607,9 @@ public class DisplayPolicy {
mStatusBarBackgroundWindows.add(win);
mStatusBarBackgroundCheckedBounds.union(sTmpRect);
if (!mStatusBarColorCheckedBounds.contains(sTmpRect)) {
- mStatusBarColorWindows.add(win);
+ mStatusBarAppearanceRegionList.add(new AppearanceRegion(
+ win.mAttrs.insetsFlags.appearance & APPEARANCE_LIGHT_STATUS_BARS,
+ new Rect(win.getFrame())));
mStatusBarColorCheckedBounds.union(sTmpRect);
}
}
@@ -1663,13 +1629,10 @@ public class DisplayPolicy {
}
}
} 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 (mStatusBar != null) {
+ addStatusBarAppearanceRegionsForDimmingWindow(
+ win.mAttrs.insetsFlags.appearance & APPEARANCE_LIGHT_STATUS_BARS,
+ mStatusBar.getFrame(), win.getBounds(), win.getFrame());
}
if (isOverlappingWithNavBar && mNavBarColorWindowCandidate == null) {
mNavBarColorWindowCandidate = win;
@@ -1677,6 +1640,48 @@ public class DisplayPolicy {
}
}
+ private void addStatusBarAppearanceRegionsForDimmingWindow(int appearance, Rect statusBarFrame,
+ Rect winBounds, Rect winFrame) {
+ if (!sTmpRect.setIntersect(winBounds, statusBarFrame)) {
+ return;
+ }
+ if (mStatusBarColorCheckedBounds.contains(sTmpRect)) {
+ return;
+ }
+ if (appearance == 0 || !sTmpRect2.setIntersect(winFrame, statusBarFrame)) {
+ mStatusBarAppearanceRegionList.add(new AppearanceRegion(0, new Rect(winBounds)));
+ mStatusBarColorCheckedBounds.union(sTmpRect);
+ return;
+ }
+ // A dimming window can divide status bar into different appearance regions (up to 3).
+ // +---------+-------------+---------+
+ // |/////////| |/////////| <-- Status Bar
+ // +---------+-------------+---------+
+ // |/////////| |/////////|
+ // |/////////| |/////////|
+ // |/////////| |/////////|
+ // |/////////| |/////////|
+ // |/////////| |/////////|
+ // +---------+-------------+---------+
+ // ^ ^ ^
+ // dim layer window dim layer
+ mStatusBarAppearanceRegionList.add(new AppearanceRegion(appearance, new Rect(winFrame)));
+ if (!sTmpRect.equals(sTmpRect2)) {
+ if (sTmpRect.height() == sTmpRect2.height()) {
+ if (sTmpRect.left != sTmpRect2.left) {
+ mStatusBarAppearanceRegionList.add(new AppearanceRegion(0, new Rect(
+ winBounds.left, winBounds.top, sTmpRect2.left, winBounds.bottom)));
+ }
+ if (sTmpRect.right != sTmpRect2.right) {
+ mStatusBarAppearanceRegionList.add(new AppearanceRegion(0, new Rect(
+ sTmpRect2.right, winBounds.top, winBounds.right, winBounds.bottom)));
+ }
+ }
+ // We don't have vertical status bar yet, so we don't handle the other orientation.
+ }
+ mStatusBarColorCheckedBounds.union(sTmpRect);
+ }
+
/**
* Called following layout of all windows and after policy has been applied
* to each window. If in this function you do
@@ -2149,18 +2154,6 @@ public class DisplayPolicy {
}
}
- /**
- * @see IWindowManager#setForwardedInsets
- */
- public void setForwardedInsets(@NonNull Insets forwardedInsets) {
- mForwardedInsets = forwardedInsets;
- }
-
- @NonNull
- public Insets getForwardedInsets() {
- return mForwardedInsets;
- }
-
@NavigationBarPosition
int navigationBarPosition(int displayRotation) {
if (mNavigationBar != null) {
@@ -2319,14 +2312,9 @@ public class DisplayPolicy {
final String focusedApp = win.mAttrs.packageName;
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()));
- }
+ final AppearanceRegion[] statusBarAppearanceRegions =
+ new AppearanceRegion[mStatusBarAppearanceRegionList.size()];
+ mStatusBarAppearanceRegionList.toArray(statusBarAppearanceRegions);
if (mLastDisableFlags != disableFlags) {
mLastDisableFlags = disableFlags;
final String cause = win.toString();
@@ -2338,7 +2326,7 @@ public class DisplayPolicy {
&& mRequestedVisibilities.equals(win.getRequestedVisibilities())
&& Objects.equals(mFocusedApp, focusedApp)
&& mLastFocusIsFullscreen == isFullscreen
- && Arrays.equals(mLastStatusBarAppearanceRegions, appearanceRegions)) {
+ && Arrays.equals(mLastStatusBarAppearanceRegions, statusBarAppearanceRegions)) {
return;
}
if (mDisplayContent.isDefaultDisplay && mLastFocusIsFullscreen != isFullscreen
@@ -2353,20 +2341,12 @@ public class DisplayPolicy {
mRequestedVisibilities = requestedVisibilities;
mFocusedApp = focusedApp;
mLastFocusIsFullscreen = isFullscreen;
- mLastStatusBarAppearanceRegions = appearanceRegions;
+ mLastStatusBarAppearanceRegions = statusBarAppearanceRegions;
callStatusBarSafely(statusBar -> statusBar.onSystemBarAttributesChanged(displayId,
- appearance, appearanceRegions, isNavbarColorManagedByIme, behavior,
+ appearance, statusBarAppearanceRegions, isNavbarColorManagedByIme, behavior,
requestedVisibilities, focusedApp));
}
- private int getStatusBarAppearance(WindowState opaque, WindowState opaqueOrDimming) {
- final boolean onKeyguard = isKeyguardShowing() && !isKeyguardOccluded();
- final WindowState colorWin = onKeyguard ? mNotificationShade : opaqueOrDimming;
- return isLightBarAllowed(colorWin, Type.statusBars()) && (colorWin == opaque || onKeyguard)
- ? (colorWin.mAttrs.insetsFlags.appearance & APPEARANCE_LIGHT_STATUS_BARS)
- : 0;
- }
-
private void callStatusBarSafely(Consumer<StatusBarManagerInternal> consumer) {
mHandler.post(() -> {
StatusBarManagerInternal statusBar = getStatusBarManagerInternal();
@@ -2760,11 +2740,10 @@ public class DisplayPolicy {
pw.print(prefix); pw.print("mNavBarBackgroundWindow=");
pw.println(mNavBarBackgroundWindow);
}
- 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 (mLastStatusBarAppearanceRegions != null) {
+ pw.print(prefix); pw.println("mLastStatusBarAppearanceRegions=");
+ for (int i = mLastStatusBarAppearanceRegions.length - 1; i >= 0; i--) {
+ pw.print(prefixInner); pw.println(mLastStatusBarAppearanceRegions[i]);
}
}
if (!mStatusBarBackgroundWindows.isEmpty()) {
diff --git a/services/core/java/com/android/server/wm/DisplayWindowListenerController.java b/services/core/java/com/android/server/wm/DisplayWindowListenerController.java
index 4141090f7fa0..276dbe9c844a 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowListenerController.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowListenerController.java
@@ -17,11 +17,14 @@
package com.android.server.wm;
import android.content.res.Configuration;
+import android.graphics.Rect;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.util.IntArray;
import android.view.IDisplayWindowListener;
+import java.util.List;
+
/**
* Manages dispatch of relevant hierarchy changes to interested listeners. Listeners are assumed
* to be remote.
@@ -116,4 +119,16 @@ class DisplayWindowListenerController {
}
mDisplayListeners.finishBroadcast();
}
+
+ void dispatchKeepClearAreasChanged(DisplayContent display, List<Rect> keepClearAreas) {
+ int count = mDisplayListeners.beginBroadcast();
+ for (int i = 0; i < count; ++i) {
+ try {
+ mDisplayListeners.getBroadcastItem(i).onKeepClearAreasChanged(
+ display.mDisplayId, keepClearAreas);
+ } catch (RemoteException e) {
+ }
+ }
+ mDisplayListeners.finishBroadcast();
+ }
}
diff --git a/services/core/java/com/android/server/wm/EmbeddedWindowController.java b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
index fc317a1212d5..0e2d84779602 100644
--- a/services/core/java/com/android/server/wm/EmbeddedWindowController.java
+++ b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
@@ -93,6 +93,18 @@ class EmbeddedWindowController {
return embeddedWindow != null ? embeddedWindow.mHostWindowState : null;
}
+ boolean isOverlay(IBinder inputToken) {
+ EmbeddedWindow embeddedWindow = mWindows.get(inputToken);
+ return embeddedWindow != null ? embeddedWindow.getIsOverlay() : false;
+ }
+
+ void setIsOverlay(IBinder inputToken) {
+ EmbeddedWindow embeddedWindow = mWindows.get(inputToken);
+ if (embeddedWindow != null) {
+ embeddedWindow.setIsOverlay();
+ }
+ }
+
void remove(IWindow client) {
for (int i = mWindows.size() - 1; i >= 0; i--) {
if (mWindows.valueAt(i).mClient.asBinder() == client.asBinder()) {
@@ -138,6 +150,12 @@ class EmbeddedWindowController {
public Session mSession;
InputChannel mInputChannel;
final int mWindowType;
+ // Track whether the EmbeddedWindow is a system hosted overlay via
+ // {@link OverlayHost}. In the case of client hosted overlays, the client
+ // view hierarchy will take care of invoking requestEmbeddedWindowFocus
+ // but for system hosted overlays we have to do this via tapOutsideDetection
+ // and this variable is mostly used for tracking that.
+ boolean mIsOverlay = false;
/**
* @param session calling session to check ownership of the window
@@ -216,5 +234,39 @@ class EmbeddedWindowController {
public int getPid() {
return mOwnerPid;
}
+
+ void setIsOverlay() {
+ mIsOverlay = true;
+ }
+ boolean getIsOverlay() {
+ return mIsOverlay;
+ }
+
+ /**
+ * System hosted overlays need the WM to invoke grantEmbeddedWindowFocus and
+ * so we need to participate inside handlePointerDownOutsideFocus logic
+ * however client hosted overlays will rely on the hosting view hierarchy
+ * to grant and revoke focus, and so the server side logic is not needed.
+ */
+ @Override
+ public boolean receiveFocusFromTapOutside() {
+ return mIsOverlay;
+ }
+
+ private void handleTap(boolean grantFocus) {
+ if (mInputChannel != null) {
+ mWmService.grantEmbeddedWindowFocus(mSession, mInputChannel.getToken(), grantFocus);
+ }
+ }
+
+ @Override
+ public void handleTapOutsideFocusOutsideSelf() {
+ handleTap(false);
+ }
+
+ @Override
+ public void handleTapOutsideFocusInsideSelf() {
+ handleTap(true);
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
index 963f3265757d..8d3e07116693 100644
--- a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
+++ b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
@@ -97,7 +97,7 @@ class EnsureActivitiesVisibleHelper {
// 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
- && mTaskFragment.isTopActivityFocusable()
+ && mTaskFragment.canBeResumed(starting)
&& (starting == null || !starting.isDescendantOf(mTaskFragment));
ArrayList<TaskFragment> adjacentTaskFragments = null;
diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java
index e02e7c5ab15d..f91969b2c558 100644
--- a/services/core/java/com/android/server/wm/InputManagerCallback.java
+++ b/services/core/java/com/android/server/wm/InputManagerCallback.java
@@ -24,6 +24,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.H.ON_POINTER_DOWN_OUTSIDE_FOCUS;
import android.annotation.NonNull;
+import android.graphics.PointF;
import android.os.Debug;
import android.os.IBinder;
import android.util.Slog;
@@ -219,6 +220,11 @@ final class InputManagerCallback implements InputManagerService.WindowManagerCal
}
@Override
+ public PointF getCursorPosition() {
+ return mService.getLatestMousePosition();
+ }
+
+ @Override
public void onPointerDownOutsideFocus(IBinder touchedToken) {
mService.mH.obtainMessage(ON_POINTER_DOWN_OUTSIDE_FOCUS, touchedToken).sendToTarget();
}
diff --git a/services/core/java/com/android/server/wm/InputTarget.java b/services/core/java/com/android/server/wm/InputTarget.java
index c7d328a2b18a..5166b8adcecc 100644
--- a/services/core/java/com/android/server/wm/InputTarget.java
+++ b/services/core/java/com/android/server/wm/InputTarget.java
@@ -36,5 +36,16 @@ interface InputTarget {
/* Owning pid of the target. */
int getPid();
+
+ /**
+ * Indicates whether a target should receive focus from server side
+ * tap outside focus detection. For example, this is false in the case of
+ * EmbeddedWindows in a client view hierarchy, where the client will do internal
+ * tap detection and invoke grantEmbeddedWindowFocus itself
+ */
+ boolean receiveFocusFromTapOutside();
+
+ void handleTapOutsideFocusInsideSelf();
+ void handleTapOutsideFocusOutsideSelf();
}
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index 1a1101e45f45..a1468cc60682 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -366,6 +366,7 @@ class InsetsStateController {
if (changed) {
notifyInsetsChanged();
mDisplayContent.updateSystemGestureExclusion();
+ mDisplayContent.updateKeepClearAreas();
mDisplayContent.getDisplayPolicy().updateSystemBarAttributes();
}
}
diff --git a/services/core/java/com/android/server/wm/OverlayHost.java b/services/core/java/com/android/server/wm/OverlayHost.java
index 14f8983571c8..724e1247b100 100644
--- a/services/core/java/com/android/server/wm/OverlayHost.java
+++ b/services/core/java/com/android/server/wm/OverlayHost.java
@@ -74,6 +74,8 @@ class OverlayHost {
requireOverlaySurfaceControl();
mOverlays.add(p);
+ mWmService.mEmbeddedWindowController.setIsOverlay(p.getInputToken());
+
SurfaceControl.Transaction t = mWmService.mTransactionFactory.get();
t.reparent(p.getSurfaceControl(), mSurfaceControl)
.show(p.getSurfaceControl());
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 5a420caa176c..d031bec5443f 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -165,6 +165,7 @@ import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Set;
@@ -986,6 +987,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
forAllDisplays(dc -> {
dc.getInputMonitor().updateInputWindowsLw(true /*force*/);
dc.updateSystemGestureExclusion();
+ dc.updateKeepClearAreas();
dc.updateTouchExcludeRegion();
});
@@ -2530,9 +2532,16 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
// Drop any cached DisplayInfos associated with this display id - the values are now
// out of date given this display changed event.
mWmService.mPossibleDisplayInfoMapper.removePossibleDisplayInfos(displayId);
+ updateDisplayImePolicyCache();
}
}
+ void updateDisplayImePolicyCache() {
+ ArrayMap<Integer, Integer> displayImePolicyMap = new ArrayMap<>();
+ forAllDisplays(dc -> displayImePolicyMap.put(dc.getDisplayId(), dc.getImePolicy()));
+ mWmService.mDisplayImePolicyCache = Collections.unmodifiableMap(displayImePolicyMap);
+ }
+
/** Update lists of UIDs that are present on displays and have access to them. */
void updateUIDsPresentOnDisplay() {
mDisplayAccessUIDs.clear();
@@ -3665,7 +3674,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
try {
if (mTaskSupervisor.realStartActivityLocked(r, mApp,
- mTop == r && r.isFocusable() /* andResume */, true /* checkConfig */)) {
+ mTop == r && r.getTask().canBeResumed(r) /* andResume */,
+ true /* checkConfig */)) {
mHasActivityStarted = true;
}
} catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 005544b961c3..7acc0c52cc4c 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -73,6 +73,7 @@ import android.view.SurfaceControl;
import android.view.SurfaceSession;
import android.view.WindowManager;
import android.window.ClientWindowFrames;
+import android.window.IOnBackInvokedCallback;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.logging.MetricsLoggerWrapper;
@@ -490,6 +491,16 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
}
}
+ @Override
+ public void reportKeepClearAreasChanged(IWindow window, List<Rect> keepClearAreas) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mService.reportKeepClearAreasChanged(this, window, keepClearAreas);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
private void actionOnWallpaper(IBinder window,
BiConsumer<WallpaperController, WindowState> action) {
final WindowState windowState = mService.windowForClientLocked(this, window, true);
@@ -863,4 +874,10 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
Binder.restoreCallingIdentity(origId);
}
}
+
+ @Override
+ public void setOnBackInvokedCallback(IWindow iWindow,
+ IOnBackInvokedCallback iOnBackInvokedCallback) throws RemoteException {
+ // TODO: Set the callback to the WindowState of the window.
+ }
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 87ef29160d71..2331dc4dff52 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -2707,10 +2707,14 @@ class Task extends TaskFragment {
}
boolean isResizeable() {
+ return isResizeable(/* checkPictureInPictureSupport */ true);
+ }
+
+ boolean isResizeable(boolean checkPictureInPictureSupport) {
final boolean forceResizable = mAtmService.mForceResizableActivities
&& getActivityType() == ACTIVITY_TYPE_STANDARD;
return forceResizable || ActivityInfo.isResizeableMode(mResizeMode)
- || mSupportsPictureInPicture;
+ || (mSupportsPictureInPicture && checkPictureInPictureSupport);
}
/**
diff --git a/services/core/java/com/android/server/wm/TaskFpsCallbackController.java b/services/core/java/com/android/server/wm/TaskFpsCallbackController.java
new file mode 100644
index 000000000000..d9dc9aa9e5e2
--- /dev/null
+++ b/services/core/java/com/android/server/wm/TaskFpsCallbackController.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import android.content.Context;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.window.IOnFpsCallbackListener;
+
+import java.util.HashMap;
+
+final class TaskFpsCallbackController {
+
+ private final Context mContext;
+ private final HashMap<IOnFpsCallbackListener, Long> mTaskFpsCallbackListeners;
+ private final HashMap<IOnFpsCallbackListener, IBinder.DeathRecipient> mDeathRecipients;
+
+ TaskFpsCallbackController(Context context) {
+ mContext = context;
+ mTaskFpsCallbackListeners = new HashMap<>();
+ mDeathRecipients = new HashMap<>();
+ }
+
+ void registerCallback(int taskId, IOnFpsCallbackListener listener) {
+ if (mTaskFpsCallbackListeners.containsKey(listener)) {
+ return;
+ }
+
+ final long nativeListener = nativeRegister(listener, taskId);
+ mTaskFpsCallbackListeners.put(listener, nativeListener);
+
+ final IBinder.DeathRecipient deathRecipient = () -> unregisterCallback(listener);
+ try {
+ listener.asBinder().linkToDeath(deathRecipient, 0);
+ mDeathRecipients.put(listener, deathRecipient);
+ } catch (RemoteException e) {
+ // ignore
+ }
+ }
+
+ void unregisterCallback(IOnFpsCallbackListener listener) {
+ if (!mTaskFpsCallbackListeners.containsKey(listener)) {
+ return;
+ }
+
+ listener.asBinder().unlinkToDeath(mDeathRecipients.get(listener), 0);
+ mDeathRecipients.remove(listener);
+
+ nativeUnregister(mTaskFpsCallbackListeners.get(listener));
+ mTaskFpsCallbackListeners.remove(listener);
+ }
+
+ private static native long nativeRegister(IOnFpsCallbackListener listener, int taskId);
+ private static native void nativeUnregister(long ptr);
+}
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index b681a96d2c0f..177d2e61d5f0 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -24,8 +24,6 @@ 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;
@@ -39,6 +37,7 @@ 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_BACK_PREVIEW;
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;
@@ -100,6 +99,7 @@ import com.android.internal.util.function.pooled.PooledPredicate;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;
@@ -254,6 +254,10 @@ class TaskFragment extends WindowContainer<WindowContainer> {
private final Rect mTmpStableBounds = new Rect();
private final Rect mTmpNonDecorBounds = new Rect();
+ //TODO(b/207481538) Remove once the infrastructure to support per-activity screenshot is
+ // implemented
+ HashMap<String, SurfaceControl.ScreenshotHardwareBuffer> mBackScreenshots = new HashMap<>();
+
private final EnsureActivitiesVisibleHelper mEnsureActivitiesVisibleHelper =
new EnsureActivitiesVisibleHelper(this);
private final EnsureVisibleActivitiesConfigHelper mEnsureVisibleActivitiesConfigHelper =
@@ -790,13 +794,8 @@ class TaskFragment extends WindowContainer<WindowContainer> {
return TASK_FRAGMENT_VISIBILITY_VISIBLE;
}
- boolean gotRootSplitScreenFragment = false;
- boolean gotOpaqueSplitScreenPrimary = false;
- boolean gotOpaqueSplitScreenSecondary = false;
boolean gotTranslucentFullscreen = false;
boolean gotTranslucentAdjacent = false;
- boolean gotTranslucentSplitScreenPrimary = false;
- boolean gotTranslucentSplitScreenSecondary = false;
boolean shouldBeVisible = true;
// This TaskFragment is only considered visible if all its parent TaskFragments are
@@ -815,8 +814,6 @@ class TaskFragment extends WindowContainer<WindowContainer> {
}
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;
@@ -864,37 +861,6 @@ class TaskFragment extends WindowContainer<WindowContainer> {
}
// 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();
@@ -920,34 +886,6 @@ class TaskFragment extends WindowContainer<WindowContainer> {
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
@@ -1683,6 +1621,7 @@ class TaskFragment extends WindowContainer<WindowContainer> {
@Override
void addChild(WindowContainer child, int index) {
+ ActivityRecord r = topRunningActivity();
mClearedTaskForReuse = false;
boolean isAddingActivity = child.asActivityRecord() != null;
@@ -1697,6 +1636,18 @@ class TaskFragment extends WindowContainer<WindowContainer> {
super.addChild(child, index);
if (isAddingActivity && task != null) {
+
+ // TODO(b/207481538): temporary per-activity screenshoting
+ if (r != null && BackNavigationController.isEnabled()) {
+ ProtoLog.v(WM_DEBUG_BACK_PREVIEW, "Screenshotting Activity %s",
+ r.mActivityComponent.flattenToString());
+ Rect outBounds = r.getBounds();
+ SurfaceControl.ScreenshotHardwareBuffer backBuffer = SurfaceControl.captureLayers(
+ r.mSurfaceControl,
+ new Rect(0, 0, outBounds.width(), outBounds.height()),
+ 1f);
+ mBackScreenshots.put(r.mActivityComponent.flattenToString(), backBuffer);
+ }
child.asActivityRecord().inHistory = true;
task.onDescendantActivityAdded(taskHadActivity, activityType, child.asActivityRecord());
}
@@ -2290,6 +2241,14 @@ class TaskFragment extends WindowContainer<WindowContainer> {
void removeChild(WindowContainer child, boolean removeSelfIfPossible) {
super.removeChild(child);
+ if (BackNavigationController.isEnabled()) {
+ //TODO(b/207481538) Remove once the infrastructure to support per-activity screenshot is
+ // implemented
+ ActivityRecord r = child.asActivityRecord();
+ if (r != null) {
+ mBackScreenshots.remove(r.mActivityComponent.flattenToString());
+ }
+ }
if (removeSelfIfPossible && (!mCreatedByOrganizer || mIsRemovalRequested) && !hasChild()) {
removeImmediately("removeLastChild " + child);
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 1ab191bb6650..b9fa29733aa6 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -16,6 +16,9 @@
package com.android.server.wm;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ClipData;
@@ -40,6 +43,7 @@ import com.android.internal.policy.KeyInterceptionInfo;
import com.android.server.input.InputManagerService;
import com.android.server.policy.WindowManagerPolicy;
+import java.lang.annotation.Retention;
import java.util.List;
import java.util.Set;
@@ -609,6 +613,7 @@ public abstract class WindowManagerInternal {
/**
* Checks whether the specified IME client has IME focus or not.
*
+ * @param windowToken The window token of the input method client
* @param uid UID of the process to be queried
* @param pid PID of the process to be queried
* @param displayId Display ID reported from the client. Note that this method also verifies
@@ -616,7 +621,22 @@ public abstract class WindowManagerInternal {
* @return {@code true} if the IME client specified with {@code uid}, {@code pid}, and
* {@code displayId} has IME focus
*/
- public abstract boolean isInputMethodClientFocus(int uid, int pid, int displayId);
+ public abstract @ImeClientFocusResult int hasInputMethodClientFocus(IBinder windowToken,
+ int uid, int pid, int displayId);
+
+ @Retention(SOURCE)
+ @IntDef({
+ ImeClientFocusResult.HAS_IME_FOCUS,
+ ImeClientFocusResult.NOT_IME_TARGET_WINDOW,
+ ImeClientFocusResult.DISPLAY_ID_MISMATCH,
+ ImeClientFocusResult.INVALID_DISPLAY_ID
+ })
+ public @interface ImeClientFocusResult {
+ int HAS_IME_FOCUS = 0;
+ int NOT_IME_TARGET_WINDOW = -1;
+ int DISPLAY_ID_MISMATCH = -2;
+ int INVALID_DISPLAY_ID = -3;
+ }
/**
* Checks whether the given {@code uid} is allowed to use the given {@code displayId} or not.
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index e4216bfb6679..026b9e1094d0 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -143,6 +143,7 @@ import android.Manifest;
import android.Manifest.permission;
import android.animation.ValueAnimator;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -170,14 +171,12 @@ import android.content.res.Resources;
import android.content.res.TypedArray;
import android.database.ContentObserver;
import android.graphics.Bitmap;
-import android.graphics.Insets;
import android.graphics.Point;
+import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.Region;
import android.hardware.configstore.V1_0.OptionalBool;
-import android.hardware.configstore.V1_1.DisplayOrientation;
import android.hardware.configstore.V1_1.ISurfaceFlingerConfigs;
-import android.hardware.configstore.V1_1.OptionalDisplayOrientation;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerInternal;
import android.hardware.input.InputManager;
@@ -229,7 +228,6 @@ import android.util.TypedValue;
import android.util.proto.ProtoOutputStream;
import android.view.Choreographer;
import android.view.Display;
-import android.view.DisplayAddress;
import android.view.DisplayInfo;
import android.view.Gravity;
import android.view.IAppTransitionAnimationSpecsFuture;
@@ -282,6 +280,7 @@ import android.view.WindowManagerPolicyConstants.PointerEventListener;
import android.view.displayhash.DisplayHash;
import android.view.displayhash.VerifiedDisplayHash;
import android.window.ClientWindowFrames;
+import android.window.IOnFpsCallbackListener;
import android.window.TaskSnapshot;
import com.android.internal.R;
@@ -439,8 +438,6 @@ public class WindowManagerService extends IWindowManager.Stub
*/
static final boolean ENABLE_FIXED_ROTATION_TRANSFORM =
SystemProperties.getBoolean("persist.wm.fixed_rotation_transform", true);
- private @Surface.Rotation int mPrimaryDisplayOrientation = Surface.ROTATION_0;
- private DisplayAddress mPrimaryDisplayPhysicalAddress;
// Enums for animation scale update types.
@Retention(RetentionPolicy.SOURCE)
@@ -589,6 +586,13 @@ public class WindowManagerService extends IWindowManager.Stub
final ArrayList<WindowState> mResizingWindows = new ArrayList<>();
/**
+ * Mapping of displayId to {@link DisplayImePolicy}.
+ * Note that this can be accessed without holding the lock.
+ */
+ volatile Map<Integer, Integer> mDisplayImePolicyCache = Collections.unmodifiableMap(
+ new ArrayMap<>());
+
+ /**
* Windows whose surface should be destroyed.
*/
final ArrayList<WindowState> mDestroySurface = new ArrayList<>();
@@ -708,6 +712,7 @@ public class WindowManagerService extends IWindowManager.Stub
final TaskSnapshotController mTaskSnapshotController;
final BlurController mBlurController;
+ final TaskFpsCallbackController mTaskFpsCallbackController;
boolean mIsTouchDevice;
boolean mIsFakeTouchDevice;
@@ -738,6 +743,8 @@ public class WindowManagerService extends IWindowManager.Stub
final WindowContextListenerController mWindowContextListenerController =
new WindowContextListenerController();
+ private InputTarget mFocusedInputTarget;
+
@VisibleForTesting
final class SettingsObserver extends ContentObserver {
private final Uri mDisplayInversionEnabledUri =
@@ -1352,6 +1359,7 @@ public class WindowManagerService extends IWindowManager.Stub
mStartingSurfaceController = new StartingSurfaceController(this);
mBlurController = new BlurController(mContext, mPowerManager);
+ mTaskFpsCallbackController = new TaskFpsCallbackController(mContext);
mAccessibilityController = new AccessibilityController(this);
}
@@ -1786,8 +1794,7 @@ public class WindowManagerService extends IWindowManager.Stub
prepareNoneTransitionForRelaunching(activity);
}
- if (displayPolicy.getLayoutHint(win.mAttrs, token, outInsetsState,
- win.isClientLocal())) {
+ if (displayPolicy.areSystemBarsForcedShownLw()) {
res |= WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_SYSTEM_BARS;
}
@@ -1834,6 +1841,7 @@ public class WindowManagerService extends IWindowManager.Stub
displayContent.getInsetsStateController().updateAboveInsetsState(
win, false /* notifyInsetsChanged */);
+ outInsetsState.set(win.getCompatInsetsState(), win.isClientLocal());
getInsetsSourceControls(win, outActiveControls);
}
@@ -2436,23 +2444,6 @@ public class WindowManagerService extends IWindowManager.Stub
configChanged = displayContent.updateOrientation();
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
- final DisplayInfo displayInfo = win.getDisplayInfo();
- int transformHint = displayInfo.rotation;
- // If the window is on the primary display, use the panel orientation to adjust the
- // transform hint
- final boolean isPrimaryDisplay = displayInfo.address != null &&
- displayInfo.address.equals(mPrimaryDisplayPhysicalAddress);
- if (isPrimaryDisplay) {
- transformHint = (transformHint + mPrimaryDisplayOrientation) % 4;
- }
- outSurfaceControl.setTransformHint(
- SurfaceControl.rotationToBufferTransform(transformHint));
- ProtoLog.v(WM_DEBUG_ORIENTATION,
- "Passing transform hint %d for window %s%s",
- transformHint, win,
- isPrimaryDisplay ? " on primary display with orientation "
- + mPrimaryDisplayOrientation : "");
-
if (toBeDisplayed && win.mIsWallpaper) {
displayContent.mWallpaperController.updateWallpaperOffset(win, false /* sync */);
}
@@ -3836,6 +3827,40 @@ public class WindowManagerService extends IWindowManager.Stub
}
/**
+ * Generates and returns an up-to-date {@link Bitmap} for the specified taskId. The returned
+ * bitmap will be full size and will not include any secure content.
+ *
+ * @param taskId The task ID of the task for which a snapshot is requested.
+ * @return The Bitmap, or null if no task with the specified ID can be found or the bitmap could
+ * not be generated.
+ */
+ @Nullable public Bitmap captureTaskBitmap(int taskId) {
+ if (mTaskSnapshotController.shouldDisableSnapshots()) {
+ return null;
+ }
+
+ synchronized (mGlobalLock) {
+ final Task task = mRoot.anyTaskForId(taskId);
+ if (task == null) {
+ return null;
+ }
+
+ task.getBounds(mTmpRect);
+ final SurfaceControl sc = task.getSurfaceControl();
+ final SurfaceControl.ScreenshotHardwareBuffer buffer = SurfaceControl.captureLayers(
+ new SurfaceControl.LayerCaptureArgs.Builder(sc)
+ .setSourceCrop(mTmpRect)
+ .build());
+ if (buffer == null) {
+ Slog.w(TAG, "Could not get screenshot buffer for taskId: " + taskId);
+ return null;
+ }
+
+ return buffer.asBitmap();
+ }
+ }
+
+ /**
* In case a task write/delete operation was lost because the system crashed, this makes sure to
* clean up the directory to remove obsolete files.
*
@@ -4314,6 +4339,15 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
+ void reportKeepClearAreasChanged(Session session, IWindow window, List<Rect> keepClearAreas) {
+ synchronized (mGlobalLock) {
+ final WindowState win = windowForClientLocked(session, window, true);
+ if (win.setKeepClearAreas(keepClearAreas)) {
+ win.getDisplayContent().updateKeepClearAreas();
+ }
+ }
+ }
+
@Override
public void registerDisplayFoldListener(IDisplayFoldListener listener) {
mPolicy.registerDisplayFoldListener(listener);
@@ -4886,9 +4920,6 @@ public class WindowManagerService extends IWindowManager.Stub
mTaskSnapshotController.systemReady();
mHasWideColorGamutSupport = queryWideColorGamutSupport();
mHasHdrSupport = queryHdrSupport();
- mPrimaryDisplayOrientation = queryPrimaryDisplayOrientation();
- mPrimaryDisplayPhysicalAddress =
- DisplayAddress.fromPhysicalDisplayId(SurfaceControl.getPrimaryPhysicalDisplayId());
UiThread.getHandler().post(mSettingsObserver::loadSettings);
IVrManager vrManager = IVrManager.Stub.asInterface(
ServiceManager.getService(Context.VR_SERVICE));
@@ -4951,39 +4982,6 @@ public class WindowManagerService extends IWindowManager.Stub
return false;
}
- private static @Surface.Rotation int queryPrimaryDisplayOrientation() {
- Optional<SurfaceFlingerProperties.primary_display_orientation_values> prop =
- SurfaceFlingerProperties.primary_display_orientation();
- if (prop.isPresent()) {
- switch (prop.get()) {
- case ORIENTATION_90: return Surface.ROTATION_90;
- case ORIENTATION_180: return Surface.ROTATION_180;
- case ORIENTATION_270: return Surface.ROTATION_270;
- case ORIENTATION_0:
- default:
- return Surface.ROTATION_0;
- }
- }
- try {
- ISurfaceFlingerConfigs surfaceFlinger = ISurfaceFlingerConfigs.getService();
- OptionalDisplayOrientation primaryDisplayOrientation =
- surfaceFlinger.primaryDisplayOrientation();
- if (primaryDisplayOrientation != null && primaryDisplayOrientation.specified) {
- switch (primaryDisplayOrientation.value) {
- case DisplayOrientation.ORIENTATION_90: return Surface.ROTATION_90;
- case DisplayOrientation.ORIENTATION_180: return Surface.ROTATION_180;
- case DisplayOrientation.ORIENTATION_270: return Surface.ROTATION_270;
- case DisplayOrientation.ORIENTATION_0:
- default:
- return Surface.ROTATION_0;
- }
- }
- } catch (Exception e) {
- // Use default value if we can't talk to config store.
- }
- return Surface.ROTATION_0;
- }
-
// Returns an input target which is mapped to the given input token. This can be a WindowState
// or an embedded window.
@Nullable InputTarget getInputTargetFromToken(IBinder inputToken) {
@@ -5011,6 +5009,7 @@ public class WindowManagerService extends IWindowManager.Stub
Slog.v(TAG_WM, "Unknown focus tokens, dropping reportFocusChanged");
return;
}
+ mFocusedInputTarget = newTarget;
mAccessibilityController.onFocusChanged(lastTarget, newTarget);
ProtoLog.i(WM_DEBUG_FOCUS_LIGHT, "Focus changing: %s -> %s", lastTarget, newTarget);
@@ -5681,6 +5680,11 @@ public class WindowManagerService extends IWindowManager.Stub
}
@Override
+ public void saveWindowTraceToFile() {
+ mWindowTracing.saveForBugreport(null /* printwriter */);
+ }
+
+ @Override
public boolean isWindowTraceEnabled() {
return mWindowTracing.isEnabled();
}
@@ -6905,6 +6909,7 @@ public class WindowManagerService extends IWindowManager.Stub
void setForceDesktopModeOnExternalDisplays(boolean forceDesktopModeOnExternalDisplays) {
synchronized (mGlobalLock) {
mForceDesktopModeOnExternalDisplays = forceDesktopModeOnExternalDisplays;
+ mRoot.updateDisplayImePolicyCache();
}
}
@@ -6960,23 +6965,6 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- @Override
- public void setForwardedInsets(int displayId, Insets insets) throws RemoteException {
- synchronized (mGlobalLock) {
- final DisplayContent dc = mRoot.getDisplayContent(displayId);
- if (dc == null) {
- return;
- }
- final int callingUid = Binder.getCallingUid();
- final int displayOwnerUid = dc.getDisplay().getOwnerUid();
- if (callingUid != displayOwnerUid) {
- throw new SecurityException(
- "Only owner of the display can set ForwardedInsets to it.");
- }
- dc.setForwardedInsets(insets);
- }
- }
-
MousePositionTracker mMousePositionTracker = new MousePositionTracker();
private static class MousePositionTracker implements PointerEventListener {
@@ -7063,6 +7051,13 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
+ PointF getLatestMousePosition() {
+ synchronized (mMousePositionTracker) {
+ return new PointF(mMousePositionTracker.mLatestMouseX,
+ mMousePositionTracker.mLatestMouseY);
+ }
+ }
+
/**
* Update a tap exclude region in the window identified by the provided id. Touches down on this
* region will not:
@@ -7329,16 +7324,14 @@ public class WindowManagerService extends IWindowManager.Stub
if (!checkCallingPermission(INTERNAL_SYSTEM_WINDOW, "getDisplayImePolicy()")) {
throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission");
}
- final DisplayContent dc = mRoot.getDisplayContent(displayId);
- if (dc == null) {
+ final Map<Integer, Integer> displayImePolicyCache = mDisplayImePolicyCache;
+ if (!displayImePolicyCache.containsKey(displayId)) {
ProtoLog.w(WM_ERROR,
"Attempted to get IME policy of a display that does not exist: %d",
displayId);
return DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
}
- synchronized (mGlobalLock) {
- return dc.getImePolicy();
- }
+ return displayImePolicyCache.get(displayId);
}
@Override
@@ -7687,19 +7680,32 @@ public class WindowManagerService extends IWindowManager.Stub
}
@Override
- public boolean isInputMethodClientFocus(int uid, int pid, int displayId) {
+ public @ImeClientFocusResult int hasInputMethodClientFocus(IBinder windowToken,
+ int uid, int pid, int displayId) {
if (displayId == Display.INVALID_DISPLAY) {
- return false;
+ return ImeClientFocusResult.INVALID_DISPLAY_ID;
}
synchronized (mGlobalLock) {
final DisplayContent displayContent = mRoot.getTopFocusedDisplayContent();
+ final WindowState window = mWindowMap.get(windowToken);
+ if (window == null) {
+ return ImeClientFocusResult.NOT_IME_TARGET_WINDOW;
+ }
+ final int tokenDisplayId = window.getDisplayContent().getDisplayId();
+ if (tokenDisplayId != displayId) {
+ Slog.e(TAG, "isInputMethodClientFocus: display ID mismatch."
+ + " from client: " + displayId
+ + " from window: " + tokenDisplayId);
+ return ImeClientFocusResult.DISPLAY_ID_MISMATCH;
+ }
if (displayContent == null
|| displayContent.getDisplayId() != displayId
|| !displayContent.hasAccess(uid)) {
- return false;
+ return ImeClientFocusResult.INVALID_DISPLAY_ID;
}
+
if (displayContent.isInputMethodClientFocus(uid, pid)) {
- return true;
+ return ImeClientFocusResult.HAS_IME_FOCUS;
}
// Okay, how about this... what is the current focus?
// It seems in some cases we may not have moved the IM
@@ -7712,10 +7718,11 @@ public class WindowManagerService extends IWindowManager.Stub
final WindowState currentFocus = displayContent.mCurrentFocus;
if (currentFocus != null && currentFocus.mSession.mUid == uid
&& currentFocus.mSession.mPid == pid) {
- return currentFocus.canBeImeTarget();
+ return currentFocus.canBeImeTarget() ? ImeClientFocusResult.HAS_IME_FOCUS
+ : ImeClientFocusResult.NOT_IME_TARGET_WINDOW;
}
}
- return false;
+ return ImeClientFocusResult.NOT_IME_TARGET_WINDOW;
}
@Override
@@ -7814,9 +7821,7 @@ public class WindowManagerService extends IWindowManager.Stub
@Override
public @DisplayImePolicy int getDisplayImePolicy(int displayId) {
- synchronized (mGlobalLock) {
- return WindowManagerService.this.getDisplayImePolicy(displayId);
- }
+ return WindowManagerService.this.getDisplayImePolicy(displayId);
}
@Override
@@ -8170,21 +8175,14 @@ public class WindowManagerService extends IWindowManager.Stub
}
private void onPointerDownOutsideFocusLocked(IBinder touchedToken) {
- WindowState touchedWindow = mInputToWindowMap.get(touchedToken);
- if (touchedWindow == null) {
- // if a user taps outside the currently focused window onto an embedded window, treat
- // it as if the host window was tapped.
- touchedWindow = mEmbeddedWindowController.getHostWindow(touchedToken);
- }
-
- if (touchedWindow == null || !touchedWindow.canReceiveKeys(true /* fromUserTouch */)) {
+ InputTarget t = getInputTargetFromToken(touchedToken);
+ if (t == null || !t.receiveFocusFromTapOutside()) {
// If the window that received the input event cannot receive keys, don't move the
// display it's on to the top since that window won't be able to get focus anyway.
return;
}
-
if (mRecentsAnimationController != null
- && mRecentsAnimationController.getTargetAppMainWindow() == touchedWindow) {
+ && mRecentsAnimationController.getTargetAppMainWindow() == t) {
// If there is an active recents animation and touched window is the target, then ignore
// the touch. The target already handles touches using its own input monitor and we
// don't want to trigger any lifecycle changes from focusing another window.
@@ -8194,13 +8192,11 @@ public class WindowManagerService extends IWindowManager.Stub
}
ProtoLog.i(WM_DEBUG_FOCUS_LIGHT, "onPointerDownOutsideFocusLocked called on %s",
- touchedWindow);
- final DisplayContent displayContent = touchedWindow.getDisplayContent();
- if (!displayContent.isOnTop()) {
- displayContent.getParent().positionChildAt(WindowContainer.POSITION_TOP, displayContent,
- true /* includingParents */);
+ t);
+ if (mFocusedInputTarget != t && mFocusedInputTarget != null) {
+ mFocusedInputTarget.handleTapOutsideFocusOutsideSelf();
}
- handleTaskFocusChange(touchedWindow.getTask(), touchedWindow.mActivityRecord);
+ t.handleTapOutsideFocusInsideSelf();
}
@VisibleForTesting
@@ -8481,6 +8477,7 @@ public class WindowManagerService extends IWindowManager.Stub
public boolean getWindowInsets(WindowManager.LayoutParams attrs, int displayId,
InsetsState outInsetsState) {
final boolean fromLocal = Binder.getCallingPid() == myPid();
+ final int uid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
@@ -8489,9 +8486,20 @@ public class WindowManagerService extends IWindowManager.Stub
throw new WindowManager.InvalidDisplayException("Display#" + displayId
+ "could not be found!");
}
- final WindowToken windowToken = dc.getWindowToken(attrs.token);
- return dc.getDisplayPolicy().getLayoutHint(attrs, windowToken, outInsetsState,
- fromLocal);
+ final WindowToken token = dc.getWindowToken(attrs.token);
+ final float overrideScale = mAtmService.mCompatModePackages.getCompatScale(
+ attrs.packageName, uid);
+ final InsetsState state = dc.getInsetsPolicy().getInsetsForWindowMetrics(attrs);
+ final boolean hasCompatScale =
+ WindowState.hasCompatScale(attrs, token, overrideScale);
+ outInsetsState.set(state, hasCompatScale || fromLocal);
+ if (hasCompatScale) {
+ final float compatScale = token != null && token.hasSizeCompatBounds()
+ ? token.getSizeCompatScale() * overrideScale
+ : overrideScale;
+ outInsetsState.scale(1f / compatScale);
+ }
+ return dc.getDisplayPolicy().areSystemBarsForcedShownLw();
}
} finally {
Binder.restoreCallingIdentity(origId);
@@ -8720,20 +8728,21 @@ public class WindowManagerService extends IWindowManager.Stub
}
boolean shouldRestoreImeVisibility(IBinder imeTargetWindowToken) {
+ final Task imeTargetWindowTask;
synchronized (mGlobalLock) {
final WindowState imeTargetWindow = mWindowMap.get(imeTargetWindowToken);
if (imeTargetWindow == null) {
return false;
}
- final Task imeTargetWindowTask = imeTargetWindow.getTask();
+ imeTargetWindowTask = imeTargetWindow.getTask();
if (imeTargetWindowTask == null) {
return false;
}
- final TaskSnapshot snapshot = getTaskSnapshot(imeTargetWindowTask.mTaskId,
- imeTargetWindowTask.mUserId, false /* isLowResolution */,
- false /* restoreFromDisk */);
- return snapshot != null && snapshot.hasImeSurface();
}
+ final TaskSnapshot snapshot = getTaskSnapshot(imeTargetWindowTask.mTaskId,
+ imeTargetWindowTask.mUserId, false /* isLowResolution */,
+ false /* restoreFromDisk */);
+ return snapshot != null && snapshot.hasImeSurface();
}
@Override
@@ -8777,4 +8786,35 @@ public class WindowManagerService extends IWindowManager.Stub
mTaskTransitionSpec = null;
}
+
+ @Override
+ @RequiresPermission(Manifest.permission.ACCESS_FPS_COUNTER)
+ public void registerTaskFpsCallback(@IntRange(from = 0) int taskId,
+ IOnFpsCallbackListener listener) {
+ if (mContext.checkCallingOrSelfPermission(Manifest.permission.ACCESS_FPS_COUNTER)
+ != PackageManager.PERMISSION_GRANTED) {
+ final int pid = Binder.getCallingPid();
+ throw new SecurityException("Access denied to process: " + pid
+ + ", must have permission " + Manifest.permission.ACCESS_FPS_COUNTER);
+ }
+
+ if (mRoot.anyTaskForId(taskId) == null) {
+ throw new IllegalArgumentException("no task with taskId: " + taskId);
+ }
+
+ mTaskFpsCallbackController.registerCallback(taskId, listener);
+ }
+
+ @Override
+ @RequiresPermission(Manifest.permission.ACCESS_FPS_COUNTER)
+ public void unregisterTaskFpsCallback(IOnFpsCallbackListener listener) {
+ if (mContext.checkCallingOrSelfPermission(Manifest.permission.ACCESS_FPS_COUNTER)
+ != PackageManager.PERMISSION_GRANTED) {
+ final int pid = Binder.getCallingPid();
+ throw new SecurityException("Access denied to process: " + pid
+ + ", must have permission " + Manifest.permission.ACCESS_FPS_COUNTER);
+ }
+
+ mTaskFpsCallbackController.unregisterCallback(listener);
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 94d4a77a7ecc..1f837677ab36 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -172,6 +172,7 @@ import static com.android.server.wm.WindowStateProto.HAS_SURFACE;
import static com.android.server.wm.WindowStateProto.IS_ON_SCREEN;
import static com.android.server.wm.WindowStateProto.IS_READY_FOR_DISPLAY;
import static com.android.server.wm.WindowStateProto.IS_VISIBLE;
+import static com.android.server.wm.WindowStateProto.KEEP_CLEAR_AREAS;
import static com.android.server.wm.WindowStateProto.PENDING_SEAMLESS_ROTATION;
import static com.android.server.wm.WindowStateProto.REMOVED;
import static com.android.server.wm.WindowStateProto.REMOVE_ON_EXIT;
@@ -196,6 +197,7 @@ import android.graphics.Matrix;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect;
+import android.graphics.RectF;
import android.graphics.Region;
import android.gui.TouchOcclusionMode;
import android.os.Binder;
@@ -442,9 +444,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// Current transformation being applied.
float mGlobalScale=1;
- float mLastGlobalScale=1;
float mInvGlobalScale=1;
- float mOverrideScale = 1;
+ final float mOverrideScale;
float mHScale=1, mVScale=1;
float mLastHScale=1, mLastVScale=1;
@@ -471,6 +472,12 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
* Coordinates are relative to the window's position.
*/
private final List<Rect> mExclusionRects = new ArrayList<>();
+ /**
+ * List of rects which should ideally not be covered by floating windows like Pip.
+ *
+ * Coordinates are relative to the window's position.
+ */
+ private final List<Rect> mKeepClearAreas = new ArrayList<>();
// 0 = left, 1 = right
private final int[] mLastRequestedExclusionHeight = {0, 0};
@@ -1012,6 +1019,55 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
}
+ /**
+ * @return a list of rects that should ideally not be covered by floating windows like pip.
+ * The returned rect coordinates are relative to the display origin.
+ */
+ List<Rect> getKeepClearAreas() {
+ final Matrix tmpMatrix = new Matrix();
+ final float[] tmpFloat9 = new float[9];
+ return getKeepClearAreas(tmpMatrix, tmpFloat9);
+ }
+
+ /**
+ * @param tmpMatrix a temporary matrix to be used for transformations
+ * @param float9 a temporary array of 9 floats
+ *
+ * @return a list of rects that should ideally not be covered by floating windows like pip.
+ * The returned rect coordinates are relative to the display origin.
+ */
+ List<Rect> getKeepClearAreas(Matrix tmpMatrix, float[] float9) {
+ getTransformationMatrix(float9, tmpMatrix);
+
+ // Translate all keep-clear rects to screen coordinates.
+ final List<Rect> transformedKeepClearAreas = new ArrayList<Rect>();
+ final RectF tmpRect = new RectF();
+ Rect curr;
+ for (Rect r : mKeepClearAreas) {
+ tmpRect.set(r);
+ tmpMatrix.mapRect(tmpRect);
+ curr = new Rect();
+ tmpRect.roundOut(curr);
+ transformedKeepClearAreas.add(curr);
+ }
+ return transformedKeepClearAreas;
+ }
+
+ /**
+ * @param keepClearAreas the new keep-clear areas for this window. The rects should be defined
+ * in window coordinate space
+ *
+ * @return true if there is a change in the list of keep-clear areas; false otherwise
+ */
+ boolean setKeepClearAreas(List<Rect> keepClearAreas) {
+ if (mKeepClearAreas.equals(keepClearAreas)) {
+ return false;
+ }
+ mKeepClearAreas.clear();
+ mKeepClearAreas.addAll(keepClearAreas);
+ return true;
+ }
+
interface PowerManagerWrapper {
void wakeUp(long time, @WakeReason int reason, String details);
@@ -1091,6 +1147,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
mSubLayer = 0;
mWinAnimator = null;
mWpcForDisplayAreaConfigChanges = null;
+ mOverrideScale = 1f;
return;
}
mDeathRecipient = deathRecipient;
@@ -1138,6 +1195,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
mLayer = 0;
mOverrideScale = mWmService.mAtmService.mCompatModePackages.getCompatScale(
mAttrs.packageName, s.mUid);
+ updateGlobalScale();
// Make sure we initial all fields before adding to parentWindow, to prevent exception
// during onDisplayChanged.
@@ -1167,6 +1225,23 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
mSession.windowAddedLocked();
}
+ boolean updateGlobalScale() {
+ if (hasCompatScale()) {
+ if (mOverrideScale != 1f) {
+ mGlobalScale = mToken.hasSizeCompatBounds()
+ ? mToken.getSizeCompatScale() * mOverrideScale
+ : mOverrideScale;
+ } else {
+ mGlobalScale = mToken.getSizeCompatScale();
+ }
+ mInvGlobalScale = 1f / mGlobalScale;
+ return true;
+ }
+
+ mGlobalScale = mInvGlobalScale = 1f;
+ return false;
+ }
+
/**
* @return {@code true} if the application runs in size compatibility mode or has an app level
* scaling override set.
@@ -1175,7 +1250,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
* @see ActivityRecord#hasSizeCompatBounds()
*/
boolean hasCompatScale() {
- return mOverrideScale != 1f || hasCompatScale(mAttrs, mActivityRecord);
+ return hasCompatScale(mAttrs, mActivityRecord, mOverrideScale);
}
/**
@@ -1183,11 +1258,16 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
* @see android.content.res.CompatibilityInfo#supportsScreen
* @see ActivityRecord#hasSizeCompatBounds()
*/
- static boolean hasCompatScale(WindowManager.LayoutParams attrs, WindowToken windowToken) {
- return (attrs.privateFlags & PRIVATE_FLAG_COMPATIBLE_WINDOW) != 0
- || (windowToken != null && windowToken.hasSizeCompatBounds()
- // Exclude starting window because it is not displayed by the application.
- && attrs.type != TYPE_APPLICATION_STARTING);
+ static boolean hasCompatScale(WindowManager.LayoutParams attrs, WindowToken token,
+ float overrideScale) {
+ if ((attrs.privateFlags & PRIVATE_FLAG_COMPATIBLE_WINDOW) != 0) {
+ return true;
+ }
+ if (attrs.type == TYPE_APPLICATION_STARTING) {
+ // Exclude starting window because it is not displayed by the application.
+ return false;
+ }
+ return token != null && token.hasSizeCompatBounds() || overrideScale != 1f;
}
/**
@@ -1691,21 +1771,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
&& (mActivityRecord.firstWindowDrawn || mActivityRecord.startingDisplayed);
}
- void prelayout() {
- if (hasCompatScale()) {
- if (mOverrideScale != 1f) {
- mGlobalScale = mToken.hasSizeCompatBounds()
- ? mToken.getSizeCompatScale() * mOverrideScale
- : mOverrideScale;
- } else {
- mGlobalScale = mToken.getSizeCompatScale();
- }
- mInvGlobalScale = 1 / mGlobalScale;
- } else {
- mGlobalScale = mInvGlobalScale = 1;
- }
- }
-
@Override
boolean hasContentToDisplay() {
if (!mAppFreezing && isDrawn() && (mViewVisibility == View.VISIBLE
@@ -2928,7 +2993,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
@Override
public void binderDied() {
try {
- boolean resetSplitScreenResizing = false;
synchronized (mWmService.mGlobalLock) {
final WindowState win = mWmService
.windowForClientLocked(mSession, mClient, false);
@@ -2944,16 +3008,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
WindowState.this.removeIfPossible();
}
}
- if (resetSplitScreenResizing) {
- try {
- // Note: this calls into ActivityManager, so we must *not* hold the window
- // manager lock while calling this.
- mWmService.mActivityTaskManager.setSplitScreenResizing(false);
- } catch (RemoteException e) {
- // Local call, shouldn't return RemoteException.
- throw e.rethrowAsRuntimeException();
- }
- }
} catch (IllegalArgumentException ex) {
// This will happen if the window has already been removed.
}
@@ -4063,6 +4117,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
proto.write(FORCE_SEAMLESS_ROTATION, mForceSeamlesslyRotate);
proto.write(HAS_COMPAT_SCALE, hasCompatScale());
proto.write(GLOBAL_SCALE, mGlobalScale);
+ for (Rect r : getKeepClearAreas()) {
+ r.dumpDebug(proto, KEEP_CLEAR_AREAS);
+ }
proto.end(token);
}
@@ -4231,6 +4288,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
pw.println(prefix + "isOnScreen=" + isOnScreen());
pw.println(prefix + "isVisible=" + isVisible());
+ pw.println(prefix + "keepClearAreas=" + getKeepClearAreas());
if (dumpAll) {
final String visibilityString = mRequestedVisibilities.toString();
if (!visibilityString.isEmpty()) {
@@ -5259,7 +5317,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
mLastVScale != newVScale ) {
getPendingTransaction().setMatrix(getSurfaceControl(),
newHScale, 0, 0, newVScale);
- mLastGlobalScale = mGlobalScale;
mLastHScale = newHScale;
mLastVScale = newVScale;
}
@@ -5985,4 +6042,24 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
boolean isTrustedOverlay() {
return mInputWindowHandle.isTrustedOverlay();
}
+
+ public boolean receiveFocusFromTapOutside() {
+ return canReceiveKeys(true);
+ }
+
+ @Override
+ public void handleTapOutsideFocusOutsideSelf() {
+ // Nothing to do here since raising the other window will naturally take care of
+ // us loosing focus
+ }
+
+ @Override
+ public void handleTapOutsideFocusInsideSelf() {
+ final DisplayContent displayContent = getDisplayContent();
+ if (!displayContent.isOnTop()) {
+ displayContent.getParent().positionChildAt(WindowContainer.POSITION_TOP, displayContent,
+ true /* includingParents */);
+ }
+ mWmService.handleTaskFocusChange(getTask(), mActivityRecord);
+ }
}
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 7b4fd365905c..79a980f0d9ee 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -68,14 +68,18 @@ cc_library_static {
"com_android_server_am_LowMemDetector.cpp",
"com_android_server_pm_PackageManagerShellCommandDataLoader.cpp",
"com_android_server_sensor_SensorService.cpp",
+ "com_android_server_wm_TaskFpsCallbackController.cpp",
"onload.cpp",
":lib_cachedAppOptimizer_native",
":lib_networkStatsFactory_native",
+ ":lib_gameManagerService_native",
],
include_dirs: [
"frameworks/base/libs",
"frameworks/native/services",
+ "frameworks/native/libs/math/include",
+ "frameworks/native/libs/ui/include",
"system/gatekeeper/include",
"system/memory/libmeminfo/include",
],
@@ -103,6 +107,7 @@ cc_defaults {
"libcrypto",
"liblog",
"libgraphicsenv",
+ "libgralloctypes",
"libhardware",
"libhardware_legacy",
"libhidlbase",
@@ -157,6 +162,10 @@ cc_defaults {
"android.hardware.gnss.measurement_corrections@1.0",
"android.hardware.gnss.visibility_control@1.0",
"android.hardware.graphics.bufferqueue@1.0",
+ "android.hardware.graphics.bufferqueue@2.0",
+ "android.hardware.graphics.common@1.2",
+ "android.hardware.graphics.common-V3-ndk",
+ "android.hardware.graphics.mapper@4.0",
"android.hardware.input.classifier@1.0",
"android.hardware.ir@1.0",
"android.hardware.light@2.0",
@@ -216,3 +225,10 @@ filegroup {
"com_android_server_am_CachedAppOptimizer.cpp",
],
}
+
+filegroup {
+ name: "lib_gameManagerService_native",
+ srcs: [
+ "com_android_server_app_GameManagerService.cpp",
+ ],
+}
diff --git a/services/core/jni/com_android_server_app_GameManagerService.cpp b/services/core/jni/com_android_server_app_GameManagerService.cpp
new file mode 100644
index 000000000000..3028813d0d5a
--- /dev/null
+++ b/services/core/jni/com_android_server_app_GameManagerService.cpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "GameManagerService"
+
+#include <android/log.h>
+#include <gui/SurfaceComposerClient.h>
+#include <log/log.h>
+#include <nativehelper/JNIHelp.h>
+
+#include "jni.h"
+
+namespace android {
+
+static void android_server_app_GameManagerService_nativeSetOverrideFrameRate(JNIEnv* env,
+ jclass clazz, jint uid,
+ jfloat frameRate) {
+ SurfaceComposerClient::setOverrideFrameRate(uid, frameRate);
+}
+
+static const JNINativeMethod gMethods[] = {
+ {"nativeSetOverrideFrameRate", "(IF)V",
+ (void*)android_server_app_GameManagerService_nativeSetOverrideFrameRate},
+};
+
+int register_android_server_app_GameManagerService(JNIEnv* env) {
+ return jniRegisterNativeMethods(env, "com/android/server/app/GameManagerService", gMethods,
+ NELEM(gMethods));
+}
+
+}; // namespace android \ No newline at end of file
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 3cd4e5ee82cf..c71686abe9de 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -286,6 +286,7 @@ public:
void requestPointerCapture(const sp<IBinder>& windowToken, bool enabled);
void setCustomPointerIcon(const SpriteIcon& icon);
void setMotionClassifierEnabled(bool enabled);
+ void notifyPointerDisplayIdChanged();
/* --- InputReaderPolicyInterface implementation --- */
@@ -1494,6 +1495,18 @@ void NativeInputManager::setMotionClassifierEnabled(bool enabled) {
mInputManager->getClassifier().setMotionClassifierEnabled(enabled);
}
+void NativeInputManager::notifyPointerDisplayIdChanged() {
+ int32_t pointerDisplayId = getPointerDisplayId();
+
+ { // acquire lock
+ AutoMutex _l(mLock);
+ mLocked.pointerDisplayId = pointerDisplayId;
+ } // release lock
+
+ mInputManager->getReader().requestRefreshConfiguration(
+ InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+}
+
// ----------------------------------------------------------------------------
static jlong nativeInit(JNIEnv* env, jclass /* clazz */,
@@ -1573,6 +1586,13 @@ static jboolean nativeHasKeys(JNIEnv* env, jclass /* clazz */,
return result;
}
+static jint nativeGetKeyCodeForKeyLocation(JNIEnv* env, jclass /* clazz */, jlong ptr,
+ jint deviceId, jint locationKeyCode) {
+ NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+ return (jint)im->getInputManager()->getReader().getKeyCodeForKeyLocation(deviceId,
+ locationKeyCode);
+}
+
static void handleInputChannelDisposed(JNIEnv* env, jobject /* inputChannelObj */,
const std::shared_ptr<InputChannel>& inputChannel,
void* data) {
@@ -2186,6 +2206,18 @@ static void nativeNotifyPortAssociationsChanged(JNIEnv* env, jclass /* clazz */,
InputReaderConfiguration::CHANGE_DISPLAY_INFO);
}
+static void nativeNotifyPointerDisplayIdChanged(JNIEnv* env, jclass /* clazz */, jlong ptr) {
+ NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+ im->notifyPointerDisplayIdChanged();
+}
+
+static void nativeSetDisplayEligibilityForPointerCapture(JNIEnv* env, jclass /* clazz */, jlong ptr,
+ jint displayId, jboolean isEligible) {
+ NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+ im->getInputManager()->getDispatcher().setDisplayEligibilityForPointerCapture(displayId,
+ isEligible);
+}
+
static void nativeChangeUniqueIdAssociation(JNIEnv* env, jclass /* clazz */, jlong ptr) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
im->getInputManager()->getReader().requestRefreshConfiguration(
@@ -2312,6 +2344,7 @@ static const JNINativeMethod gInputManagerMethods[] = {
{"nativeGetKeyCodeState", "(JIII)I", (void*)nativeGetKeyCodeState},
{"nativeGetSwitchState", "(JIII)I", (void*)nativeGetSwitchState},
{"nativeHasKeys", "(JII[I[Z)Z", (void*)nativeHasKeys},
+ {"nativeGetKeyCodeForKeyLocation", "(JII)I", (void*)nativeGetKeyCodeForKeyLocation},
{"nativeCreateInputChannel", "(JLjava/lang/String;)Landroid/view/InputChannel;",
(void*)nativeCreateInputChannel},
{"nativeCreateInputMonitor", "(JIZLjava/lang/String;I)Landroid/view/InputChannel;",
@@ -2370,6 +2403,9 @@ static const JNINativeMethod gInputManagerMethods[] = {
{"nativeCanDispatchToDisplay", "(JII)Z", (void*)nativeCanDispatchToDisplay},
{"nativeNotifyPortAssociationsChanged", "(J)V", (void*)nativeNotifyPortAssociationsChanged},
{"nativeChangeUniqueIdAssociation", "(J)V", (void*)nativeChangeUniqueIdAssociation},
+ {"nativeNotifyPointerDisplayIdChanged", "(J)V", (void*)nativeNotifyPointerDisplayIdChanged},
+ {"nativeSetDisplayEligibilityForPointerCapture", "(JIZ)V",
+ (void*)nativeSetDisplayEligibilityForPointerCapture},
{"nativeSetMotionClassifierEnabled", "(JZ)V", (void*)nativeSetMotionClassifierEnabled},
{"nativeGetSensorList", "(JI)[Landroid/hardware/input/InputSensorInfo;",
(void*)nativeGetSensorList},
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index be656e3f3a27..0da8f7ef0dea 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -51,6 +51,7 @@
#include "android_runtime/AndroidRuntime.h"
#include "android_runtime/Log.h"
#include "gnss/AGnss.h"
+#include "gnss/AGnssRil.h"
#include "gnss/GnssAntennaInfoCallback.h"
#include "gnss/GnssBatching.h"
#include "gnss/GnssConfiguration.h"
@@ -76,8 +77,6 @@ static jmethodID method_setGnssHardwareModelName;
static jmethodID method_psdsDownloadRequest;
static jmethodID method_reportNiNotification;
static jmethodID method_requestLocation;
-static jmethodID method_requestRefLocation;
-static jmethodID method_requestSetID;
static jmethodID method_requestUtcTime;
static jmethodID method_reportGnssServiceDied;
static jmethodID method_reportGnssPowerStats;
@@ -126,7 +125,6 @@ using android::hardware::hidl_string;
using android::hardware::hidl_death_recipient;
using android::hardware::gnss::V1_0::GnssLocationFlags;
-using android::hardware::gnss::V1_0::IAGnssRilCallback;
using android::hardware::gnss::V1_0::IGnssNavigationMessage;
using android::hardware::gnss::V1_0::IGnssNavigationMessageCallback;
using android::hardware::gnss::V1_0::IGnssNi;
@@ -158,8 +156,6 @@ using IGnssCallback_V1_0 = android::hardware::gnss::V1_0::IGnssCallback;
using IGnssCallback_V2_0 = android::hardware::gnss::V2_0::IGnssCallback;
using IGnssCallback_V2_1 = android::hardware::gnss::V2_1::IGnssCallback;
using IGnssAntennaInfo = android::hardware::gnss::V2_1::IGnssAntennaInfo;
-using IAGnssRil_V1_0 = android::hardware::gnss::V1_0::IAGnssRil;
-using IAGnssRil_V2_0 = android::hardware::gnss::V2_0::IAGnssRil;
using IMeasurementCorrections_V1_0 = android::hardware::gnss::measurement_corrections::V1_0::IMeasurementCorrections;
using IMeasurementCorrections_V1_1 = android::hardware::gnss::measurement_corrections::V1_1::IMeasurementCorrections;
@@ -175,7 +171,9 @@ using android::hardware::gnss::GnssPowerStats;
using android::hardware::gnss::IGnssPowerIndication;
using android::hardware::gnss::IGnssPowerIndicationCallback;
using android::hardware::gnss::PsdsType;
+
using IAGnssAidl = android::hardware::gnss::IAGnss;
+using IAGnssRilAidl = android::hardware::gnss::IAGnssRil;
using IGnssAidl = android::hardware::gnss::IGnss;
using IGnssCallbackAidl = android::hardware::gnss::IGnssCallback;
using IGnssBatchingAidl = android::hardware::gnss::IGnssBatching;
@@ -208,8 +206,6 @@ sp<IGnssAidl> gnssHalAidl = nullptr;
sp<IGnssBatchingAidl> gnssBatchingAidlIface = nullptr;
sp<IGnssPsdsAidl> gnssPsdsAidlIface = nullptr;
sp<IGnssXtra> gnssXtraIface = nullptr;
-sp<IAGnssRil_V1_0> agnssRilIface = nullptr;
-sp<IAGnssRil_V2_0> agnssRilIface_V2_0 = nullptr;
sp<IGnssNi> gnssNiIface = nullptr;
sp<IGnssPowerIndication> gnssPowerIndicationIface = nullptr;
sp<IMeasurementCorrections_V1_0> gnssCorrectionsIface_V1_0 = nullptr;
@@ -224,6 +220,7 @@ std::unique_ptr<android::gnss::GnssBatchingInterface> gnssBatchingIface = nullpt
std::unique_ptr<android::gnss::GnssGeofenceInterface> gnssGeofencingIface = nullptr;
std::unique_ptr<android::gnss::AGnssInterface> agnssIface = nullptr;
std::unique_ptr<android::gnss::GnssDebugInterface> gnssDebugIface = nullptr;
+std::unique_ptr<android::gnss::AGnssRilInterface> agnssRilIface = nullptr;
#define WAKE_LOCK_NAME "GPS"
@@ -909,29 +906,6 @@ Return<bool> GnssVisibilityControlCallback::isInEmergencySession() {
return result;
}
-/*
- * AGnssRilCallback implements the callback methods required by the AGnssRil
- * interface.
- */
-struct AGnssRilCallback : IAGnssRilCallback {
- Return<void> requestSetIdCb(uint32_t setIdFlag) override;
- Return<void> requestRefLocCb() override;
-};
-
-Return<void> AGnssRilCallback::requestSetIdCb(uint32_t setIdFlag) {
- JNIEnv* env = getJniEnv();
- env->CallVoidMethod(mCallbacksObj, method_requestSetID, setIdFlag);
- checkAndClearExceptionFromCallback(env, __FUNCTION__);
- return Void();
-}
-
-Return<void> AGnssRilCallback::requestRefLocCb() {
- JNIEnv* env = getJniEnv();
- env->CallVoidMethod(mCallbacksObj, method_requestRefLocation);
- checkAndClearExceptionFromCallback(env, __FUNCTION__);
- return Void();
-}
-
/* Initializes the GNSS service handle. */
static void android_location_gnss_hal_GnssNative_set_gps_service_handle() {
gnssHalAidl = waitForVintfService<IGnssAidl>();
@@ -990,8 +964,6 @@ static void android_location_gnss_hal_GnssNative_class_init_once(JNIEnv* env, jc
method_reportNiNotification = env->GetMethodID(clazz, "reportNiNotification",
"(IIIIILjava/lang/String;Ljava/lang/String;II)V");
method_requestLocation = env->GetMethodID(clazz, "requestLocation", "(ZZ)V");
- method_requestRefLocation = env->GetMethodID(clazz, "requestRefLocation", "()V");
- method_requestSetID = env->GetMethodID(clazz, "requestSetID", "(I)V");
method_requestUtcTime = env->GetMethodID(clazz, "requestUtcTime", "()V");
method_reportGnssServiceDied = env->GetMethodID(clazz, "reportGnssServiceDied", "()V");
method_reportNfwNotification = env->GetMethodID(clazz, "reportNfwNotification",
@@ -1069,6 +1041,7 @@ static void android_location_gnss_hal_GnssNative_class_init_once(JNIEnv* env, jc
gnss::GnssMeasurement_class_init_once(env, clazz);
gnss::GnssNavigationMessage_class_init_once(env, clazz);
gnss::AGnss_class_init_once(env, clazz);
+ gnss::AGnssRil_class_init_once(env, clazz);
gnss::Utils_class_init_once(env);
}
@@ -1124,20 +1097,21 @@ static void android_location_gnss_hal_GnssNative_init_once(JNIEnv* env, jobject
}
}
- if (gnssHal_V2_0 != nullptr) {
+ if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
+ sp<IAGnssRilAidl> agnssRilAidl;
+ auto status = gnssHalAidl->getExtensionAGnssRil(&agnssRilAidl);
+ if (checkAidlStatus(status, "Unable to get a handle to AGnssRil interface.")) {
+ agnssRilIface = std::make_unique<gnss::AGnssRil>(agnssRilAidl);
+ }
+ } else if (gnssHal_V2_0 != nullptr) {
auto agnssRil_V2_0 = gnssHal_V2_0->getExtensionAGnssRil_2_0();
- if (!agnssRil_V2_0.isOk()) {
- ALOGD("Unable to get a handle to AGnssRil_V2_0");
- } else {
- agnssRilIface_V2_0 = agnssRil_V2_0;
- agnssRilIface = agnssRilIface_V2_0;
+ if (checkHidlReturn(agnssRil_V2_0, "Unable to get a handle to AGnssRil_V2_0")) {
+ agnssRilIface = std::make_unique<gnss::AGnssRil_V2_0>(agnssRil_V2_0);
}
} else if (gnssHal != nullptr) {
auto agnssRil_V1_0 = gnssHal->getExtensionAGnssRil();
- if (!agnssRil_V1_0.isOk()) {
- ALOGD("Unable to get a handle to AGnssRil");
- } else {
- agnssRilIface = agnssRil_V1_0;
+ if (checkHidlReturn(agnssRil_V1_0, "Unable to get a handle to AGnssRil_V1_0")) {
+ agnssRilIface = std::make_unique<gnss::AGnssRil_V1_0>(agnssRil_V1_0);
}
}
@@ -1472,12 +1446,9 @@ static jboolean android_location_gnss_hal_GnssNative_init(JNIEnv* /* env */, jcl
ALOGI("Unable to initialize IGnssNi interface.");
}
- // Set IAGnssRil.hal callback.
- sp<IAGnssRilCallback> aGnssRilCbIface = new AGnssRilCallback();
- if (agnssRilIface != nullptr) {
- auto status = agnssRilIface->setCallback(aGnssRilCbIface);
- checkHidlReturn(status, "IAGnssRil setCallback() failed.");
- } else {
+ // Set IAGnssRil callback.
+ if (agnssRilIface == nullptr ||
+ !agnssRilIface->setCallback(std::make_unique<gnss::AGnssRilCallback>())) {
ALOGI("Unable to initialize IAGnssRil interface.");
}
@@ -1605,31 +1576,13 @@ static void android_location_gnss_hal_GnssNative_delete_aiding_data(JNIEnv* /* e
}
static void android_location_gnss_hal_GnssNative_agps_set_reference_location_cellid(
- JNIEnv* /* env */, jclass, jint type, jint mcc, jint mnc, jint lac, jint cid) {
- IAGnssRil_V1_0::AGnssRefLocation location;
-
+ JNIEnv* /* env */, jclass, jint type, jint mcc, jint mnc, jint lac, jlong cid, jint tac,
+ jint pcid, jint arfcn) {
if (agnssRilIface == nullptr) {
ALOGE("%s: IAGnssRil interface not available.", __func__);
return;
}
-
- switch (static_cast<IAGnssRil_V1_0::AGnssRefLocationType>(type)) {
- case IAGnssRil_V1_0::AGnssRefLocationType::GSM_CELLID:
- case IAGnssRil_V1_0::AGnssRefLocationType::UMTS_CELLID:
- location.type = static_cast<IAGnssRil_V1_0::AGnssRefLocationType>(type);
- location.cellID.mcc = mcc;
- location.cellID.mnc = mnc;
- location.cellID.lac = lac;
- location.cellID.cid = cid;
- break;
- default:
- ALOGE("Neither a GSM nor a UMTS cellid (%s:%d).", __FUNCTION__, __LINE__);
- return;
- break;
- }
-
- auto result = agnssRilIface->setRefLocation(location);
- checkHidlReturn(result, "IAGnssRil setRefLocation() failed.");
+ agnssRilIface->setRefLocation(type, mcc, mnc, lac, cid, tac, pcid, arfcn);
}
static void android_location_gnss_hal_GnssNative_agps_set_id(JNIEnv* env, jclass, jint type,
@@ -1638,10 +1591,7 @@ static void android_location_gnss_hal_GnssNative_agps_set_id(JNIEnv* env, jclass
ALOGE("%s: IAGnssRil interface not available.", __func__);
return;
}
-
- ScopedJniString jniSetId{env, setid_string};
- auto result = agnssRilIface->setSetId((IAGnssRil_V1_0::SetIDType)type, jniSetId);
- checkHidlReturn(result, "IAGnssRil setSetId() failed.");
+ agnssRilIface->setSetId(type, setid_string);
}
static jint android_location_gnss_hal_GnssNative_read_nmea(JNIEnv* env, jclass,
@@ -1878,31 +1828,12 @@ static void android_location_GnssNetworkConnectivityHandler_update_network_state
jstring apn,
jlong networkHandle,
jshort capabilities) {
- if (agnssRilIface_V2_0 != nullptr) {
- ScopedJniString jniApn{env, apn};
- IAGnssRil_V2_0::NetworkAttributes networkAttributes = {
- .networkHandle = static_cast<uint64_t>(networkHandle),
- .isConnected = static_cast<bool>(connected),
- .capabilities = static_cast<uint16_t>(capabilities),
- .apn = jniApn
- };
-
- auto result = agnssRilIface_V2_0->updateNetworkState_2_0(networkAttributes);
- checkHidlReturn(result, "IAGnssRil updateNetworkState_2_0() failed.");
- } else if (agnssRilIface != nullptr) {
- ScopedJniString jniApn{env, apn};
- hidl_string hidlApn{jniApn};
- auto result = agnssRilIface->updateNetworkState(connected,
- static_cast<IAGnssRil_V1_0::NetworkType>(type), roaming);
- checkHidlReturn(result, "IAGnssRil updateNetworkState() failed.");
-
- if (!hidlApn.empty()) {
- result = agnssRilIface->updateNetworkAvailability(available, hidlApn);
- checkHidlReturn(result, "IAGnssRil updateNetworkAvailability() failed.");
- }
- } else {
+ if (agnssRilIface == nullptr) {
ALOGE("%s: IAGnssRil interface not available.", __func__);
+ return;
}
+ agnssRilIface->updateNetworkState(connected, type, roaming, available, apn, networkHandle,
+ capabilities);
}
static jboolean android_location_gnss_hal_GnssNative_is_geofence_supported(JNIEnv* /* env */,
@@ -2342,11 +2273,12 @@ static void android_location_gnss_hal_GnssNative_cleanup_batching(JNIEnv*, jclas
}
static jboolean android_location_gnss_hal_GnssNative_start_batch(JNIEnv*, jclass, jlong periodNanos,
+ jfloat minUpdateDistanceMeters,
jboolean wakeOnFifoFull) {
if (gnssBatchingIface == nullptr) {
return JNI_FALSE; // batching not supported
}
- return gnssBatchingIface->start(periodNanos, wakeOnFifoFull);
+ return gnssBatchingIface->start(periodNanos, minUpdateDistanceMeters, wakeOnFifoFull);
}
static void android_location_gnss_hal_GnssNative_flush_batch(JNIEnv*, jclass) {
@@ -2418,7 +2350,7 @@ static const JNINativeMethod sLocationProviderMethods[] = {
reinterpret_cast<void*>(android_location_gnss_hal_GnssNative_inject_psds_data)},
{"native_agps_set_id", "(ILjava/lang/String;)V",
reinterpret_cast<void*>(android_location_gnss_hal_GnssNative_agps_set_id)},
- {"native_agps_set_ref_location_cellid", "(IIIII)V",
+ {"native_agps_set_ref_location_cellid", "(IIIIJIII)V",
reinterpret_cast<void*>(
android_location_gnss_hal_GnssNative_agps_set_reference_location_cellid)},
{"native_set_agps_server", "(ILjava/lang/String;I)V",
@@ -2436,7 +2368,7 @@ static const JNINativeMethod sBatchingMethods[] = {
/* name, signature, funcPtr */
{"native_get_batch_size", "()I",
reinterpret_cast<void*>(android_location_gnss_hal_GnssNative_get_batch_size)},
- {"native_start_batch", "(JZ)Z",
+ {"native_start_batch", "(JFZ)Z",
reinterpret_cast<void*>(android_location_gnss_hal_GnssNative_start_batch)},
{"native_flush_batch", "()V",
reinterpret_cast<void*>(android_location_gnss_hal_GnssNative_flush_batch)},
diff --git a/services/core/jni/com_android_server_vibrator_VibratorController.cpp b/services/core/jni/com_android_server_vibrator_VibratorController.cpp
index b484796af6e2..f5e6c45c75b8 100644
--- a/services/core/jni/com_android_server_vibrator_VibratorController.cpp
+++ b/services/core/jni/com_android_server_vibrator_VibratorController.cpp
@@ -39,8 +39,8 @@ namespace android {
static JavaVM* sJvm = nullptr;
static jmethodID sMethodIdOnComplete;
-static jclass sFrequencyMappingClass;
-static jmethodID sFrequencyMappingCtor;
+static jclass sFrequencyProfileClass;
+static jmethodID sFrequencyProfileCtor;
static struct {
jmethodID setCapabilities;
jmethodID setSupportedEffects;
@@ -51,7 +51,7 @@ static struct {
jmethodID setPrimitiveDelayMax;
jmethodID setCompositionSizeMax;
jmethodID setQFactor;
- jmethodID setFrequencyMapping;
+ jmethodID setFrequencyProfile;
} sVibratorInfoBuilderClassInfo;
static struct {
jfieldID id;
@@ -437,11 +437,11 @@ static jboolean vibratorGetInfo(JNIEnv* env, jclass /* clazz */, jlong ptr,
env->SetFloatArrayRegion(maxAmplitudes, 0, amplitudes.size(),
reinterpret_cast<jfloat*>(amplitudes.data()));
}
- jobject frequencyMapping =
- env->NewObject(sFrequencyMappingClass, sFrequencyMappingCtor, resonantFrequency,
+ jobject frequencyProfile =
+ env->NewObject(sFrequencyProfileClass, sFrequencyProfileCtor, resonantFrequency,
minFrequency, frequencyResolution, maxAmplitudes);
- env->CallObjectMethod(vibratorInfoBuilder, sVibratorInfoBuilderClassInfo.setFrequencyMapping,
- frequencyMapping);
+ env->CallObjectMethod(vibratorInfoBuilder, sVibratorInfoBuilderClassInfo.setFrequencyProfile,
+ frequencyProfile);
return info.isFailedLogged("vibratorGetInfo") ? JNI_FALSE : JNI_TRUE;
}
@@ -485,9 +485,9 @@ int register_android_server_vibrator_VibratorController(JavaVM* jvm, JNIEnv* env
sRampClassInfo.endFrequencyHz = GetFieldIDOrDie(env, rampClass, "mEndFrequencyHz", "F");
sRampClassInfo.duration = GetFieldIDOrDie(env, rampClass, "mDuration", "I");
- jclass frequencyMappingClass = FindClassOrDie(env, "android/os/VibratorInfo$FrequencyMapping");
- sFrequencyMappingClass = static_cast<jclass>(env->NewGlobalRef(frequencyMappingClass));
- sFrequencyMappingCtor = GetMethodIDOrDie(env, sFrequencyMappingClass, "<init>", "(FFF[F)V");
+ jclass frequencyProfileClass = FindClassOrDie(env, "android/os/VibratorInfo$FrequencyProfile");
+ sFrequencyProfileClass = static_cast<jclass>(env->NewGlobalRef(frequencyProfileClass));
+ sFrequencyProfileCtor = GetMethodIDOrDie(env, sFrequencyProfileClass, "<init>", "(FFF[F)V");
jclass vibratorInfoBuilderClass = FindClassOrDie(env, "android/os/VibratorInfo$Builder");
sVibratorInfoBuilderClassInfo.setCapabilities =
@@ -517,9 +517,9 @@ int register_android_server_vibrator_VibratorController(JavaVM* jvm, JNIEnv* env
sVibratorInfoBuilderClassInfo.setQFactor =
GetMethodIDOrDie(env, vibratorInfoBuilderClass, "setQFactor",
"(F)Landroid/os/VibratorInfo$Builder;");
- sVibratorInfoBuilderClassInfo.setFrequencyMapping =
- GetMethodIDOrDie(env, vibratorInfoBuilderClass, "setFrequencyMapping",
- "(Landroid/os/VibratorInfo$FrequencyMapping;)"
+ sVibratorInfoBuilderClassInfo.setFrequencyProfile =
+ GetMethodIDOrDie(env, vibratorInfoBuilderClass, "setFrequencyProfile",
+ "(Landroid/os/VibratorInfo$FrequencyProfile;)"
"Landroid/os/VibratorInfo$Builder;");
return jniRegisterNativeMethods(env,
diff --git a/core/jni/android_view_SurfaceControlFpsListener.cpp b/services/core/jni/com_android_server_wm_TaskFpsCallbackController.cpp
index 0b15acd77689..0202306fc395 100644
--- a/core/jni/android_view_SurfaceControlFpsListener.cpp
+++ b/services/core/jni/com_android_server_wm_TaskFpsCallbackController.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 2021 The Android Open Source Project
+ * Copyright 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define LOG_TAG "SurfaceControlFpsListener"
+#define LOG_TAG "TaskFpsCallbackController"
#include <android/gui/BnFpsListener.h>
#include <android_runtime/AndroidRuntime.h>
@@ -35,11 +35,10 @@ namespace {
struct {
jclass mClass;
jmethodID mDispatchOnFpsReported;
-} gListenerClassInfo;
+} gCallbackClassInfo;
-struct SurfaceControlFpsListener : public gui::BnFpsListener {
- SurfaceControlFpsListener(JNIEnv* env, jobject listener)
- : mListener(env->NewWeakGlobalRef(listener)) {}
+struct TaskFpsCallback : public gui::BnFpsListener {
+ TaskFpsCallback(JNIEnv* env, jobject listener) : mListener(env->NewWeakGlobalRef(listener)) {}
binder::Status onFpsReported(float fps) override {
JNIEnv* env = AndroidRuntime::getJNIEnv();
@@ -50,13 +49,13 @@ struct SurfaceControlFpsListener : public gui::BnFpsListener {
// Weak reference went out of scope
return binder::Status::ok();
}
- env->CallStaticVoidMethod(gListenerClassInfo.mClass,
- gListenerClassInfo.mDispatchOnFpsReported, listener,
+ env->CallStaticVoidMethod(gCallbackClassInfo.mClass,
+ gCallbackClassInfo.mDispatchOnFpsReported, listener,
static_cast<jfloat>(fps));
env->DeleteGlobalRef(listener);
if (env->ExceptionCheck()) {
- ALOGE("SurfaceControlFpsListener.onFpsReported() failed.");
+ ALOGE("TaskFpsCallback.onFpsReported() failed.");
LOGE_EX(env);
env->ExceptionClear();
}
@@ -64,7 +63,7 @@ struct SurfaceControlFpsListener : public gui::BnFpsListener {
}
protected:
- virtual ~SurfaceControlFpsListener() {
+ virtual ~TaskFpsCallback() {
JNIEnv* env = AndroidRuntime::getJNIEnv();
env->DeleteWeakGlobalRef(mListener);
}
@@ -73,55 +72,48 @@ private:
jweak mListener;
};
-jlong nativeCreate(JNIEnv* env, jclass clazz, jobject obj) {
- SurfaceControlFpsListener* listener = new SurfaceControlFpsListener(env, obj);
- listener->incStrong((void*)nativeCreate);
- return reinterpret_cast<jlong>(listener);
-}
-
-void nativeDestroy(JNIEnv* env, jclass clazz, jlong ptr) {
- SurfaceControlFpsListener* listener = reinterpret_cast<SurfaceControlFpsListener*>(ptr);
- listener->decStrong((void*)nativeCreate);
-}
+jlong nativeRegister(JNIEnv* env, jclass clazz, jobject obj, jint taskId) {
+ TaskFpsCallback* callback = new TaskFpsCallback(env, obj);
-void nativeRegister(JNIEnv* env, jclass clazz, jlong ptr, jint taskId) {
- sp<SurfaceControlFpsListener> listener = reinterpret_cast<SurfaceControlFpsListener*>(ptr);
- if (SurfaceComposerClient::addFpsListener(taskId, listener) != OK) {
+ if (SurfaceComposerClient::addFpsListener(taskId, callback) != OK) {
constexpr auto error_msg = "Couldn't addFpsListener";
ALOGE(error_msg);
jniThrowRuntimeException(env, error_msg);
}
+ callback->incStrong((void*)nativeRegister);
+
+ return reinterpret_cast<jlong>(callback);
}
void nativeUnregister(JNIEnv* env, jclass clazz, jlong ptr) {
- sp<SurfaceControlFpsListener> listener = reinterpret_cast<SurfaceControlFpsListener*>(ptr);
+ sp<TaskFpsCallback> callback = reinterpret_cast<TaskFpsCallback*>(ptr);
- if (SurfaceComposerClient::removeFpsListener(listener) != OK) {
+ if (SurfaceComposerClient::removeFpsListener(callback) != OK) {
constexpr auto error_msg = "Couldn't removeFpsListener";
ALOGE(error_msg);
jniThrowRuntimeException(env, error_msg);
}
+
+ callback->decStrong((void*)nativeRegister);
}
-const JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */
- {"nativeCreate", "(Landroid/view/SurfaceControlFpsListener;)J", (void*)nativeCreate},
- {"nativeDestroy", "(J)V", (void*)nativeDestroy},
- {"nativeRegister", "(JI)V", (void*)nativeRegister},
+ {"nativeRegister", "(Landroid/window/IOnFpsCallbackListener;I)J", (void*)nativeRegister},
{"nativeUnregister", "(J)V", (void*)nativeUnregister}};
} // namespace
-int register_android_view_SurfaceControlFpsListener(JNIEnv* env) {
- int res = jniRegisterNativeMethods(env, "android/view/SurfaceControlFpsListener", gMethods,
- NELEM(gMethods));
+int register_com_android_server_wm_TaskFpsCallbackController(JNIEnv* env) {
+ int res = jniRegisterNativeMethods(env, "com/android/server/wm/TaskFpsCallbackController",
+ gMethods, NELEM(gMethods));
LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register native methods.");
- jclass clazz = env->FindClass("android/view/SurfaceControlFpsListener");
- gListenerClassInfo.mClass = MakeGlobalRefOrDie(env, clazz);
- gListenerClassInfo.mDispatchOnFpsReported =
+ jclass clazz = env->FindClass("android/window/TaskFpsCallback");
+ gCallbackClassInfo.mClass = MakeGlobalRefOrDie(env, clazz);
+ gCallbackClassInfo.mDispatchOnFpsReported =
env->GetStaticMethodID(clazz, "dispatchOnFpsReported",
- "(Landroid/view/SurfaceControlFpsListener;F)V");
+ "(Landroid/window/IOnFpsCallbackListener;F)V");
return 0;
}
diff --git a/services/core/jni/gnss/AGnssRil.cpp b/services/core/jni/gnss/AGnssRil.cpp
new file mode 100644
index 000000000000..d760b4d2195e
--- /dev/null
+++ b/services/core/jni/gnss/AGnssRil.cpp
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Define LOG_TAG before <log/log.h> to overwrite the default value.
+#define LOG_TAG "AGnssRilJni"
+
+#include "AGnssRil.h"
+
+#include "Utils.h"
+
+using android::hardware::gnss::IAGnssRil;
+using IAGnssRil_V1_0 = android::hardware::gnss::V1_0::IAGnssRil;
+using IAGnssRil_V2_0 = android::hardware::gnss::V2_0::IAGnssRil;
+
+namespace android::gnss {
+
+// Implementation of AGnssRil (AIDL HAL)
+
+AGnssRil::AGnssRil(const sp<IAGnssRil>& iAGnssRil) : mIAGnssRil(iAGnssRil) {
+ assert(mIAGnssRil != nullptr);
+}
+
+jboolean AGnssRil::setCallback(const std::unique_ptr<AGnssRilCallback>& callback) {
+ auto status = mIAGnssRil->setCallback(callback->getAidl());
+ return checkAidlStatus(status, "IAGnssRilAidl setCallback() failed.");
+}
+
+jboolean AGnssRil::setSetId(jint type, const jstring& setid_string) {
+ JNIEnv* env = getJniEnv();
+ ScopedJniString jniSetId{env, setid_string};
+ auto status = mIAGnssRil->setSetId((IAGnssRil::SetIDType)type, jniSetId.c_str());
+ return checkAidlStatus(status, "IAGnssRilAidl setSetId() failed.");
+}
+
+jboolean AGnssRil::setRefLocation(jint type, jint mcc, jint mnc, jint lac, jlong cid, jint tac,
+ jint pcid, jint arfcn) {
+ IAGnssRil::AGnssRefLocation location;
+ location.type = static_cast<IAGnssRil::AGnssRefLocationType>(type);
+
+ switch (location.type) {
+ case IAGnssRil::AGnssRefLocationType::GSM_CELLID:
+ case IAGnssRil::AGnssRefLocationType::UMTS_CELLID:
+ case IAGnssRil::AGnssRefLocationType::LTE_CELLID:
+ case IAGnssRil::AGnssRefLocationType::NR_CELLID:
+ location.cellID.mcc = mcc;
+ location.cellID.mnc = mnc;
+ location.cellID.lac = lac;
+ location.cellID.cid = cid;
+ location.cellID.tac = tac;
+ location.cellID.pcid = pcid;
+ location.cellID.arfcn = arfcn;
+ break;
+ default:
+ ALOGE("Unknown cellid (%s:%d).", __FUNCTION__, __LINE__);
+ return JNI_FALSE;
+ break;
+ }
+
+ auto status = mIAGnssRil->setRefLocation(location);
+ return checkAidlStatus(status, "IAGnssRilAidl dataConnClosed() failed.");
+}
+
+jboolean AGnssRil::updateNetworkState(jboolean connected, jint type, jboolean roaming,
+ jboolean available, const jstring& apn, jlong networkHandle,
+ jshort capabilities) {
+ JNIEnv* env = getJniEnv();
+ ScopedJniString jniApn{env, apn};
+ IAGnssRil::NetworkAttributes networkAttributes;
+ networkAttributes.networkHandle = static_cast<int64_t>(networkHandle),
+ networkAttributes.isConnected = static_cast<bool>(connected),
+ networkAttributes.capabilities = static_cast<int32_t>(capabilities),
+ networkAttributes.apn = jniApn.c_str();
+
+ auto result = mIAGnssRil->updateNetworkState(networkAttributes);
+ return checkAidlStatus(result, "IAGnssRilAidl updateNetworkState() failed.");
+}
+
+// Implementation of AGnssRil_V1_0
+
+AGnssRil_V1_0::AGnssRil_V1_0(const sp<IAGnssRil_V1_0>& iAGnssRil) : mAGnssRil_V1_0(iAGnssRil) {
+ assert(mIAGnssRil_V1_0 != nullptr);
+}
+
+jboolean AGnssRil_V1_0::setCallback(const std::unique_ptr<AGnssRilCallback>& callback) {
+ auto result = mAGnssRil_V1_0->setCallback(callback->getV1_0());
+ return checkHidlReturn(result, "IAGnssRil_V1_0 setCallback() failed.");
+}
+
+jboolean AGnssRil_V1_0::setSetId(jint type, const jstring& setid_string) {
+ JNIEnv* env = getJniEnv();
+ ScopedJniString jniSetId{env, setid_string};
+ auto result = mAGnssRil_V1_0->setSetId((IAGnssRil_V1_0::SetIDType)type, jniSetId);
+ return checkHidlReturn(result, "IAGnssRil_V1_0 setSetId() failed.");
+}
+
+jboolean AGnssRil_V1_0::setRefLocation(jint type, jint mcc, jint mnc, jint lac, jlong cid, jint,
+ jint, jint) {
+ IAGnssRil_V1_0::AGnssRefLocation location;
+ switch (static_cast<IAGnssRil_V1_0::AGnssRefLocationType>(type)) {
+ case IAGnssRil_V1_0::AGnssRefLocationType::GSM_CELLID:
+ case IAGnssRil_V1_0::AGnssRefLocationType::UMTS_CELLID:
+ location.type = static_cast<IAGnssRil_V1_0::AGnssRefLocationType>(type);
+ location.cellID.mcc = mcc;
+ location.cellID.mnc = mnc;
+ location.cellID.lac = lac;
+ location.cellID.cid = cid;
+ break;
+ default:
+ ALOGE("Neither a GSM nor a UMTS cellid (%s:%d).", __FUNCTION__, __LINE__);
+ return JNI_FALSE;
+ break;
+ }
+
+ auto result = mAGnssRil_V1_0->setRefLocation(location);
+ return checkHidlReturn(result, "IAGnssRil_V1_0 setRefLocation() failed.");
+}
+
+jboolean AGnssRil_V1_0::updateNetworkState(jboolean connected, jint type, jboolean roaming,
+ jboolean available, const jstring& apn,
+ jlong networkHandle, jshort capabilities) {
+ JNIEnv* env = getJniEnv();
+ ScopedJniString jniApn{env, apn};
+ hardware::hidl_string hidlApn{jniApn};
+ hardware::Return<bool> result(false);
+
+ if (!hidlApn.empty()) {
+ result = mAGnssRil_V1_0->updateNetworkAvailability(available, hidlApn);
+ checkHidlReturn(result, "IAGnssRil_V1_0 updateNetworkAvailability() failed.");
+ }
+
+ result = mAGnssRil_V1_0->updateNetworkState(connected,
+ static_cast<IAGnssRil_V1_0::NetworkType>(type),
+ roaming);
+ return checkHidlReturn(result, "IAGnssRil_V1_0 updateNetworkState() failed.");
+}
+
+// Implementation of AGnssRil_V2_0
+
+AGnssRil_V2_0::AGnssRil_V2_0(const sp<IAGnssRil_V2_0>& iAGnssRil)
+ : AGnssRil_V1_0{iAGnssRil}, mAGnssRil_V2_0(iAGnssRil) {
+ assert(mIAGnssRil_V2_0 != nullptr);
+}
+
+jboolean AGnssRil_V2_0::updateNetworkState(jboolean connected, jint type, jboolean roaming,
+ jboolean available, const jstring& apn,
+ jlong networkHandle, jshort capabilities) {
+ JNIEnv* env = getJniEnv();
+ ScopedJniString jniApn{env, apn};
+ IAGnssRil_V2_0::NetworkAttributes networkAttributes =
+ {.networkHandle = static_cast<uint64_t>(networkHandle),
+ .isConnected = static_cast<bool>(connected),
+ .capabilities = static_cast<uint16_t>(capabilities),
+ .apn = jniApn.c_str()};
+
+ auto result = mAGnssRil_V2_0->updateNetworkState_2_0(networkAttributes);
+ return checkHidlReturn(result, "AGnssRil_V2_0 updateNetworkState_2_0() failed.");
+}
+
+} // namespace android::gnss
diff --git a/services/core/jni/gnss/AGnssRil.h b/services/core/jni/gnss/AGnssRil.h
new file mode 100644
index 000000000000..ce14a77d56c4
--- /dev/null
+++ b/services/core/jni/gnss/AGnssRil.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ANDROID_SERVER_GNSS_AGNSSRIL_H
+#define _ANDROID_SERVER_GNSS_AGNSSRIL_H
+
+#pragma once
+
+#ifndef LOG_TAG
+#error LOG_TAG must be defined before including this file.
+#endif
+
+#include <android/hardware/gnss/1.0/IAGnssRil.h>
+#include <android/hardware/gnss/2.0/IAGnssRil.h>
+#include <android/hardware/gnss/BnAGnssRil.h>
+#include <log/log.h>
+
+#include "AGnssRilCallback.h"
+#include "jni.h"
+
+namespace android::gnss {
+
+class AGnssRilInterface {
+public:
+ virtual ~AGnssRilInterface() {}
+ virtual jboolean setCallback(const std::unique_ptr<AGnssRilCallback>& callback) = 0;
+ virtual jboolean setSetId(jint type, const jstring& setid_string) = 0;
+ virtual jboolean setRefLocation(jint type, jint mcc, jint mnc, jint lac, jlong cid, jint tac,
+ jint pcid, jint arfcn) = 0;
+ virtual jboolean updateNetworkState(jboolean connected, jint type, jboolean roaming,
+ jboolean available, const jstring& apn, jlong networkHandle,
+ jshort capabilities) = 0;
+};
+
+class AGnssRil : public AGnssRilInterface {
+public:
+ AGnssRil(const sp<android::hardware::gnss::IAGnssRil>& iAGnssRil);
+ jboolean setCallback(const std::unique_ptr<AGnssRilCallback>& callback) override;
+ jboolean setSetId(jint type, const jstring& setid_string) override;
+ jboolean setRefLocation(jint type, jint mcc, jint mnc, jint lac, jlong cid, jint tac, jint pcid,
+ jint arfcn) override;
+ jboolean updateNetworkState(jboolean connected, jint type, jboolean roaming, jboolean available,
+ const jstring& apn, jlong networkHandle,
+ jshort capabilities) override;
+
+private:
+ const sp<android::hardware::gnss::IAGnssRil> mIAGnssRil;
+};
+
+class AGnssRil_V1_0 : public AGnssRilInterface {
+public:
+ AGnssRil_V1_0(const sp<android::hardware::gnss::V1_0::IAGnssRil>& iAGnssRil);
+ jboolean setCallback(const std::unique_ptr<AGnssRilCallback>& callback) override;
+ jboolean setSetId(jint type, const jstring& setid_string) override;
+ jboolean setRefLocation(jint type, jint mcc, jint mnc, jint lac, jlong cid, jint, jint,
+ jint) override;
+ jboolean updateNetworkState(jboolean connected, jint type, jboolean roaming, jboolean available,
+ const jstring& apn, jlong networkHandle,
+ jshort capabilities) override;
+
+private:
+ const sp<android::hardware::gnss::V1_0::IAGnssRil> mAGnssRil_V1_0;
+};
+
+class AGnssRil_V2_0 : public AGnssRil_V1_0 {
+public:
+ AGnssRil_V2_0(const sp<android::hardware::gnss::V2_0::IAGnssRil>& iAGnssRil);
+ jboolean updateNetworkState(jboolean connected, jint type, jboolean roaming, jboolean available,
+ const jstring& apn, jlong networkHandle,
+ jshort capabilities) override;
+
+private:
+ const sp<android::hardware::gnss::V2_0::IAGnssRil> mAGnssRil_V2_0;
+};
+
+} // namespace android::gnss
+
+#endif // _ANDROID_SERVER_GNSS_AGNSSRIL_H
diff --git a/services/core/jni/gnss/AGnssRilCallback.cpp b/services/core/jni/gnss/AGnssRilCallback.cpp
new file mode 100644
index 000000000000..b63ccc281aa9
--- /dev/null
+++ b/services/core/jni/gnss/AGnssRilCallback.cpp
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "AGnssRilCbJni"
+
+#include "AGnssRilCallback.h"
+
+namespace android::gnss {
+
+jmethodID method_requestSetID;
+jmethodID method_requestRefLocation;
+
+using binder::Status;
+using hardware::Return;
+using hardware::Void;
+
+void AGnssRil_class_init_once(JNIEnv* env, jclass clazz) {
+ method_requestSetID = env->GetMethodID(clazz, "requestSetID", "(I)V");
+ method_requestRefLocation = env->GetMethodID(clazz, "requestRefLocation", "()V");
+}
+
+Status AGnssRilCallbackAidl::requestSetIdCb(int setIdflag) {
+ AGnssRilCallbackUtil::requestSetIdCb(setIdflag);
+ return Status::ok();
+}
+
+Status AGnssRilCallbackAidl::requestRefLocCb() {
+ AGnssRilCallbackUtil::requestRefLocCb();
+ return Status::ok();
+}
+
+Return<void> AGnssRilCallback_V1_0::requestSetIdCb(uint32_t setIdflag) {
+ AGnssRilCallbackUtil::requestSetIdCb(setIdflag);
+ return Void();
+}
+
+Return<void> AGnssRilCallback_V1_0::requestRefLocCb() {
+ AGnssRilCallbackUtil::requestRefLocCb();
+ return Void();
+}
+
+void AGnssRilCallbackUtil::requestSetIdCb(int setIdflag) {
+ ALOGD("%s. setIdflag: %d, ", __func__, setIdflag);
+ JNIEnv* env = getJniEnv();
+ env->CallVoidMethod(mCallbacksObj, method_requestSetID, setIdflag);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+}
+
+void AGnssRilCallbackUtil::requestRefLocCb() {
+ ALOGD("%s.", __func__);
+ JNIEnv* env = getJniEnv();
+ env->CallVoidMethod(mCallbacksObj, method_requestRefLocation);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+}
+
+} // namespace android::gnss
diff --git a/services/core/jni/gnss/AGnssRilCallback.h b/services/core/jni/gnss/AGnssRilCallback.h
new file mode 100644
index 000000000000..2d12089fd6e3
--- /dev/null
+++ b/services/core/jni/gnss/AGnssRilCallback.h
@@ -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.
+ */
+
+#ifndef _ANDROID_SERVER_GNSS_AGNSSRILCALLBACK_H
+#define _ANDROID_SERVER_GNSS_AGNSSRILCALLBACK_H
+
+#pragma once
+
+#ifndef LOG_TAG
+#error LOG_TAG must be defined before including this file.
+#endif
+
+#include <android/hardware/gnss/1.0/IAGnssRil.h>
+#include <android/hardware/gnss/BnAGnssRilCallback.h>
+#include <log/log.h>
+
+#include "Utils.h"
+#include "jni.h"
+
+namespace android::gnss {
+
+void AGnssRil_class_init_once(JNIEnv* env, jclass clazz);
+
+/*
+ * AGnssRilCallbackAidl class implements the callback methods required by the
+ * android::hardware::gnss::IAGnssRil interface.
+ */
+class AGnssRilCallbackAidl : public android::hardware::gnss::BnAGnssRilCallback {
+public:
+ binder::Status requestSetIdCb(int setIdflag) override;
+ binder::Status requestRefLocCb() override;
+};
+
+/*
+ * AGnssRilCallback_V1_0 implements callback methods required by the IAGnssRilCallback 1.0
+ * interface.
+ */
+class AGnssRilCallback_V1_0 : public android::hardware::gnss::V1_0::IAGnssRilCallback {
+public:
+ // Methods from ::android::hardware::gps::V1_0::IAGnssRilCallback follow.
+ hardware::Return<void> requestSetIdCb(uint32_t setIdflag) override;
+ hardware::Return<void> requestRefLocCb() override;
+};
+
+class AGnssRilCallback {
+public:
+ AGnssRilCallback() {}
+ sp<AGnssRilCallbackAidl> getAidl() {
+ if (callbackAidl == nullptr) {
+ callbackAidl = sp<AGnssRilCallbackAidl>::make();
+ }
+ return callbackAidl;
+ }
+
+ sp<AGnssRilCallback_V1_0> getV1_0() {
+ if (callbackV1_0 == nullptr) {
+ callbackV1_0 = sp<AGnssRilCallback_V1_0>::make();
+ }
+ return callbackV1_0;
+ }
+
+private:
+ sp<AGnssRilCallbackAidl> callbackAidl;
+ sp<AGnssRilCallback_V1_0> callbackV1_0;
+};
+
+struct AGnssRilCallbackUtil {
+ static void requestSetIdCb(int setIdflag);
+ static void requestRefLocCb();
+
+private:
+ AGnssRilCallbackUtil() = delete;
+};
+
+} // namespace android::gnss
+
+#endif // _ANDROID_SERVER_GNSS_AGNSSRILCALLBACK_H \ No newline at end of file
diff --git a/services/core/jni/gnss/Android.bp b/services/core/jni/gnss/Android.bp
index 63f5f526db17..d8de5a604b3d 100644
--- a/services/core/jni/gnss/Android.bp
+++ b/services/core/jni/gnss/Android.bp
@@ -25,6 +25,8 @@ cc_library_shared {
srcs: [
"AGnss.cpp",
"AGnssCallback.cpp",
+ "AGnssRil.cpp",
+ "AGnssRilCallback.cpp",
"GnssAntennaInfoCallback.cpp",
"GnssBatching.cpp",
"GnssBatchingCallback.cpp",
diff --git a/services/core/jni/gnss/GnssBatching.cpp b/services/core/jni/gnss/GnssBatching.cpp
index b66bf21381c7..7f936b9a510d 100644
--- a/services/core/jni/gnss/GnssBatching.cpp
+++ b/services/core/jni/gnss/GnssBatching.cpp
@@ -47,9 +47,12 @@ jint GnssBatching::getBatchSize() {
return size;
}
-jboolean GnssBatching::start(long periodNanos, bool wakeOnFifoFull) {
- int flags = (wakeOnFifoFull) ? IGnssBatching::WAKEUP_ON_FIFO_FULL : 0;
- auto status = mIGnssBatching->start(periodNanos, flags);
+jboolean GnssBatching::start(long periodNanos, float minUpdateDistanceMeters, bool wakeOnFifoFull) {
+ IGnssBatching::Options options;
+ options.flags = (wakeOnFifoFull) ? IGnssBatching::WAKEUP_ON_FIFO_FULL : 0;
+ options.periodNanos = periodNanos;
+ options.minDistanceMeters = minUpdateDistanceMeters;
+ auto status = mIGnssBatching->start(options);
return checkAidlStatus(status, "IGnssBatchingAidl start() failed.");
}
@@ -88,9 +91,13 @@ jint GnssBatching_V1_0::getBatchSize() {
return static_cast<jint>(result);
}
-jboolean GnssBatching_V1_0::start(long periodNanos, bool wakeOnFifoFull) {
+jboolean GnssBatching_V1_0::start(long periodNanos, float minUpdateDistanceMeters,
+ bool wakeOnFifoFull) {
IGnssBatching_V1_0::Options options;
options.periodNanos = periodNanos;
+ if (minUpdateDistanceMeters > 0) {
+ ALOGW("minUpdateDistanceMeters is not supported in 1.0 GNSS HAL.");
+ }
if (wakeOnFifoFull) {
options.flags = static_cast<uint8_t>(IGnssBatching_V1_0::Flag::WAKEUP_ON_FIFO_FULL);
} else {
diff --git a/services/core/jni/gnss/GnssBatching.h b/services/core/jni/gnss/GnssBatching.h
index a98ca9b0e492..eda02ce39551 100644
--- a/services/core/jni/gnss/GnssBatching.h
+++ b/services/core/jni/gnss/GnssBatching.h
@@ -38,7 +38,8 @@ public:
virtual ~GnssBatchingInterface() {}
virtual jboolean init(const std::unique_ptr<GnssBatchingCallback>& callback) = 0;
virtual jint getBatchSize() = 0;
- virtual jboolean start(long periodNanos, bool wakeupOnFifoFull) = 0;
+ virtual jboolean start(long periodNanos, float minUpdateDistanceMeters,
+ bool wakeupOnFifoFull) = 0;
virtual jboolean stop() = 0;
virtual jboolean flush() = 0;
virtual jboolean cleanup() = 0;
@@ -49,7 +50,7 @@ public:
GnssBatching(const sp<android::hardware::gnss::IGnssBatching>& iGnssBatching);
jboolean init(const std::unique_ptr<GnssBatchingCallback>& callback) override;
jint getBatchSize() override;
- jboolean start(long periodNanos, bool wakeupOnFifoFull) override;
+ jboolean start(long periodNanos, float minUpdateDistanceMeters, bool wakeupOnFifoFull) override;
jboolean stop() override;
jboolean flush() override;
jboolean cleanup() override;
@@ -63,7 +64,7 @@ public:
GnssBatching_V1_0(const sp<android::hardware::gnss::V1_0::IGnssBatching>& iGnssBatching);
jboolean init(const std::unique_ptr<GnssBatchingCallback>& callback) override;
jint getBatchSize() override;
- jboolean start(long periodNanos, bool wakeupOnFifoFull) override;
+ jboolean start(long periodNanos, float minUpdateDistanceMeters, bool wakeupOnFifoFull) override;
jboolean stop() override;
jboolean flush() override;
jboolean cleanup() override;
diff --git a/services/core/jni/gnss/GnssMeasurementCallback.cpp b/services/core/jni/gnss/GnssMeasurementCallback.cpp
index 5a7cee9db5bb..fbdeec6b897e 100644
--- a/services/core/jni/gnss/GnssMeasurementCallback.cpp
+++ b/services/core/jni/gnss/GnssMeasurementCallback.cpp
@@ -366,6 +366,7 @@ void GnssMeasurementCallbackAidl::translateAndSetGnssData(const GnssData& data)
env->DeleteLocalRef(clock);
env->DeleteLocalRef(measurementArray);
+ env->DeleteLocalRef(gnssAgcArray);
}
void GnssMeasurementCallbackAidl::translateSingleGnssMeasurement(JNIEnv* env,
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index d339ef1154c5..ba5b3f54efa1 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -64,6 +64,8 @@ int register_android_server_GpuService(JNIEnv* env);
int register_android_server_stats_pull_StatsPullAtomService(JNIEnv* env);
int register_android_server_sensor_SensorService(JavaVM* vm, JNIEnv* env);
int register_android_server_companion_virtual_InputController(JNIEnv* env);
+int register_android_server_app_GameManagerService(JNIEnv* env);
+int register_com_android_server_wm_TaskFpsCallbackController(JNIEnv* env);
};
using namespace android;
@@ -121,5 +123,7 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
register_android_server_stats_pull_StatsPullAtomService(env);
register_android_server_sensor_SensorService(vm, env);
register_android_server_companion_virtual_InputController(env);
+ register_android_server_app_GameManagerService(env);
+ register_com_android_server_wm_TaskFpsCallbackController(env);
return JNI_VERSION_1_4;
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
index df9ab5003122..f19202aff2f0 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
@@ -145,6 +145,10 @@ class ActiveAdmin {
private static final String TAG_PREFERENTIAL_NETWORK_SERVICE_ENABLED =
"preferential-network-service-enabled";
private static final String TAG_USB_DATA_SIGNALING = "usb-data-signaling";
+ private static final String TAG_WIFI_MIN_SECURITY = "wifi-min-security";
+ private static final String TAG_SSID_ALLOWLIST = "ssid-allowlist";
+ private static final String TAG_SSID_DENYLIST = "ssid-denylist";
+ private static final String TAG_SSID = "ssid";
private static final String ATTR_VALUE = "value";
private static final String ATTR_LAST_NETWORK_LOGGING_NOTIFICATION = "last-notification";
private static final String ATTR_NUM_NETWORK_LOGGING_NOTIFICATIONS = "num-notifications";
@@ -237,6 +241,14 @@ class ActiveAdmin {
// List of package names to keep cached.
List<String> keepUninstalledPackages;
+ // The allowlist of SSIDs the device may connect to.
+ // By default, the allowlist restriction is deactivated.
+ List<String> mSsidAllowlist;
+
+ // The denylist of SSIDs the device may not connect to.
+ // By default, the denylist restriction is deactivated.
+ List<String> mSsidDenylist;
+
// TODO: review implementation decisions with frameworks team
boolean specifiesGlobalProxy = false;
String globalProxySpec = null;
@@ -298,6 +310,8 @@ class ActiveAdmin {
private static final boolean USB_DATA_SIGNALING_ENABLED_DEFAULT = true;
boolean mUsbDataSignalingEnabled = USB_DATA_SIGNALING_ENABLED_DEFAULT;
+ int mWifiMinimumSecurityLevel = DevicePolicyManager.WIFI_SECURITY_OPEN;
+
ActiveAdmin(DeviceAdminInfo info, boolean isParent) {
this.info = info;
this.isParent = isParent;
@@ -574,6 +588,15 @@ class ActiveAdmin {
if (mUsbDataSignalingEnabled != USB_DATA_SIGNALING_ENABLED_DEFAULT) {
writeAttributeValueToXml(out, TAG_USB_DATA_SIGNALING, mUsbDataSignalingEnabled);
}
+ if (mWifiMinimumSecurityLevel != DevicePolicyManager.WIFI_SECURITY_OPEN) {
+ writeAttributeValueToXml(out, TAG_WIFI_MIN_SECURITY, mWifiMinimumSecurityLevel);
+ }
+ if (mSsidAllowlist != null && !mSsidAllowlist.isEmpty()) {
+ writeAttributeValuesToXml(out, TAG_SSID_ALLOWLIST, TAG_SSID, mSsidAllowlist);
+ }
+ if (mSsidDenylist != null && !mSsidDenylist.isEmpty()) {
+ writeAttributeValuesToXml(out, TAG_SSID_DENYLIST, TAG_SSID, mSsidDenylist);
+ }
}
void writeTextToXml(TypedXmlSerializer out, String tag, String text) throws IOException {
@@ -826,6 +849,14 @@ class ActiveAdmin {
} else if (TAG_USB_DATA_SIGNALING.equals(tag)) {
mUsbDataSignalingEnabled = parser.getAttributeBoolean(null, ATTR_VALUE,
USB_DATA_SIGNALING_ENABLED_DEFAULT);
+ } else if (TAG_WIFI_MIN_SECURITY.equals(tag)) {
+ mWifiMinimumSecurityLevel = parser.getAttributeInt(null, ATTR_VALUE);
+ } else if (TAG_SSID_ALLOWLIST.equals(tag)) {
+ mSsidAllowlist = new ArrayList<>();
+ readAttributeValues(parser, TAG_SSID, mSsidAllowlist);
+ } else if (TAG_SSID_DENYLIST.equals(tag)) {
+ mSsidDenylist = new ArrayList<>();
+ readAttributeValues(parser, TAG_SSID, mSsidDenylist);
} else {
Slogf.w(LOG_TAG, "Unknown admin tag: %s", tag);
XmlUtils.skipCurrentTag(parser);
@@ -1184,5 +1215,14 @@ class ActiveAdmin {
pw.print("mUsbDataSignaling=");
pw.println(mUsbDataSignalingEnabled);
+
+ pw.print("mWifiMinimumSecurityLevel=");
+ pw.println(mWifiMinimumSecurityLevel);
+
+ pw.print("mSsidAllowlist=");
+ pw.println(mSsidAllowlist);
+
+ pw.print("mSsidDenylist=");
+ pw.println(mSsidDenylist);
}
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index ebe9f9327032..9b87b9d6104b 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -19,6 +19,7 @@ import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.app.admin.DevicePolicyDrawableResource;
import android.app.admin.DevicePolicySafetyChecker;
+import android.app.admin.DevicePolicyStringResource;
import android.app.admin.FullyManagedDeviceProvisioningParams;
import android.app.admin.IDevicePolicyManager;
import android.app.admin.ManagedProfileProvisioningParams;
@@ -177,4 +178,15 @@ abstract class BaseIDevicePolicyManager extends IDevicePolicyManager.Stub {
int drawableId, int drawableStyle, int drawableSource) {
return null;
}
+
+ @Override
+ public void setStrings(@NonNull List<DevicePolicyStringResource> strings){}
+
+ @Override
+ public void resetStrings(String[] stringIds){}
+
+ @Override
+ public ParcelableResource getString(String stringId) {
+ return null;
+ }
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceManagementResourcesProvider.java b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceManagementResourcesProvider.java
index 534229402888..9a982357afca 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceManagementResourcesProvider.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceManagementResourcesProvider.java
@@ -20,6 +20,7 @@ import static android.app.admin.DevicePolicyResources.Drawable.Source.UPDATABLE_
import static android.app.admin.DevicePolicyResources.Drawable.Style;
import static android.app.admin.DevicePolicyResources.Drawable.Style.UPDATABLE_DRAWABLE_STYLES;
import static android.app.admin.DevicePolicyResources.Drawable.UPDATABLE_DRAWABLE_IDS;
+import static android.app.admin.DevicePolicyResources.Strings.UPDATABLE_STRING_IDS;
import static java.util.Objects.requireNonNull;
@@ -27,6 +28,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.admin.DevicePolicyDrawableResource;
import android.app.admin.DevicePolicyResources;
+import android.app.admin.DevicePolicyStringResource;
import android.app.admin.ParcelableResource;
import android.os.Environment;
import android.util.AtomicFile;
@@ -64,14 +66,26 @@ class DeviceManagementResourcesProvider {
private static final String ATTR_DRAWABLE_STYLE = "drawable-style";
private static final String ATTR_DRAWABLE_SOURCE = "drawable-source";
private static final String ATTR_DRAWABLE_ID = "drawable-id";
+ private static final String TAG_STRING_ENTRY = "string-entry";
+ private static final String ATTR_SOURCE_ID = "source-id";
-
+ /**
+ * Map of <drawable_id, <style_id, resource_value>>
+ */
private final Map<Integer, Map<Integer, ParcelableResource>>
mUpdatedDrawablesForStyle = new HashMap<>();
+ /**
+ * Map of <drawable_id, <source_id, resource_value>>
+ */
private final Map<Integer, Map<Integer, ParcelableResource>>
mUpdatedDrawablesForSource = new HashMap<>();
+ /**
+ * Map of <string_id, resource_value>
+ */
+ private final Map<String, ParcelableResource> mUpdatedStrings = new HashMap<>();
+
private final Object mLock = new Object();
private final Injector mInjector;
@@ -114,12 +128,10 @@ class DeviceManagementResourcesProvider {
private boolean updateDrawable(
int drawableId, int drawableStyle, ParcelableResource updatableResource) {
if (!UPDATABLE_DRAWABLE_IDS.contains(drawableId)) {
- throw new IllegalArgumentException(
- "Can't update drawable resource, invalid drawable " + "id " + drawableId);
+ Log.w(TAG, "Updating a resource for an unknown drawable id " + drawableId);
}
if (!UPDATABLE_DRAWABLE_STYLES.contains(drawableStyle)) {
- throw new IllegalArgumentException(
- "Can't update drawable resource, invalid style id " + drawableStyle);
+ Log.w(TAG, "Updating a resource for an unknown style id " + drawableStyle);
}
synchronized (mLock) {
if (!mUpdatedDrawablesForStyle.containsKey(drawableId)) {
@@ -139,12 +151,10 @@ class DeviceManagementResourcesProvider {
private boolean updateDrawableForSource(
int drawableId, int drawableSource, ParcelableResource updatableResource) {
if (!UPDATABLE_DRAWABLE_IDS.contains(drawableId)) {
- throw new IllegalArgumentException("Can't update drawable resource, invalid drawable "
- + "id " + drawableId);
+ Log.w(TAG, "Updating a resource for an unknown drawable id " + drawableId);
}
if (!UPDATABLE_DRAWABLE_SOURCES.contains(drawableSource)) {
- throw new IllegalArgumentException("Can't update drawable resource, invalid source id "
- + drawableSource);
+ Log.w(TAG, "Updating a resource for an unknown source id " + drawableSource);
}
synchronized (mLock) {
if (!mUpdatedDrawablesForSource.containsKey(drawableId)) {
@@ -183,19 +193,15 @@ class DeviceManagementResourcesProvider {
ParcelableResource getDrawable(
int drawableId, int drawableStyle, int drawableSource) {
if (!UPDATABLE_DRAWABLE_IDS.contains(drawableId)) {
- Log.e(TAG, "Can't get updated drawable resource, invalid drawable id "
- + drawableId);
- return null;
+ Log.w(TAG, "Getting an updated resource for an unknown drawable id " + drawableId);
}
if (!UPDATABLE_DRAWABLE_STYLES.contains(drawableStyle)) {
- Log.e(TAG, "Can't get updated drawable resource, invalid style id "
+ Log.w(TAG, "Getting an updated resource for an unknown drawable style "
+ drawableStyle);
- return null;
}
if (!UPDATABLE_DRAWABLE_SOURCES.contains(drawableSource)) {
- Log.e(TAG, "Can't get updated drawable resource, invalid source id "
+ Log.w(TAG, "Getting an updated resource for an unknown drawable Source "
+ drawableSource);
- return null;
}
if (mUpdatedDrawablesForSource.containsKey(drawableId)
&& mUpdatedDrawablesForSource.get(drawableId).containsKey(drawableSource)) {
@@ -216,6 +222,73 @@ class DeviceManagementResourcesProvider {
return null;
}
+ /**
+ * Returns {@code false} if no resources were updated.
+ */
+ boolean updateStrings(@NonNull List<DevicePolicyStringResource> strings) {
+ boolean updated = false;
+ for (int i = 0; i < strings.size(); i++) {
+ String stringId = strings.get(i).getStringId();
+ ParcelableResource resource = strings.get(i).getResource();
+
+ Objects.requireNonNull(resource, "ParcelableResource must be provided.");
+ updated |= updateString(stringId, resource);
+ }
+ if (!updated) {
+ return false;
+ }
+ synchronized (mLock) {
+ write();
+ return true;
+ }
+ }
+
+ private boolean updateString(String stringId, ParcelableResource updatableResource) {
+ if (!UPDATABLE_STRING_IDS.contains(stringId)) {
+ Log.w(TAG, "Updating a resource for an unknown string id " + stringId);
+ }
+ synchronized (mLock) {
+ ParcelableResource current = mUpdatedStrings.get(stringId);
+ if (updatableResource.equals(current)) {
+ return false;
+ }
+ mUpdatedStrings.put(stringId, updatableResource);
+ return true;
+ }
+ }
+
+ /**
+ * Returns {@code false} if no resources were removed.
+ */
+ boolean removeStrings(@NonNull String[] stringIds) {
+ synchronized (mLock) {
+ boolean removed = false;
+ for (int i = 0; i < stringIds.length; i++) {
+ String stringId = stringIds[i];
+ removed |= mUpdatedStrings.remove(stringId) != null;
+ }
+ if (!removed) {
+ return false;
+ }
+ write();
+ return true;
+ }
+ }
+
+ @Nullable
+ ParcelableResource getString(String stringId) {
+ if (!UPDATABLE_STRING_IDS.contains(stringId)) {
+ Log.w(TAG, "Getting an updated resource for an unknown string id " + stringId);
+ }
+
+ if (mUpdatedStrings.containsKey(stringId)) {
+ return mUpdatedStrings.get(stringId);
+ }
+
+ Log.d(TAG, "No updated string found for string id " + stringId);
+ return null;
+ }
+
private void write() {
Log.d(TAG, "Writing updated resources to file.");
new ResourcesReaderWriter().writeToFileLocked();
@@ -362,6 +435,18 @@ class DeviceManagementResourcesProvider {
out.endTag(/* namespace= */ null, TAG_DRAWABLE_SOURCE_ENTRY);
}
}
+ if (mUpdatedStrings != null && !mUpdatedStrings.isEmpty()) {
+ for (Map.Entry<String, ParcelableResource> entry
+ : mUpdatedStrings.entrySet()) {
+ out.startTag(/* namespace= */ null, TAG_STRING_ENTRY);
+ out.attribute(
+ /* namespace= */ null,
+ ATTR_SOURCE_ID,
+ entry.getKey());
+ entry.getValue().writeToXmlFile(out);
+ out.endTag(/* namespace= */ null, TAG_STRING_ENTRY);
+ }
+ }
}
private boolean readInner(
@@ -401,6 +486,12 @@ class DeviceManagementResourcesProvider {
ParcelableResource.createFromXml(parser));
}
break;
+ case TAG_STRING_ENTRY:
+ String sourceId = parser.getAttributeValue(
+ /* namespace= */ null, ATTR_SOURCE_ID);
+ mUpdatedStrings.put(
+ sourceId, ParcelableResource.createFromXml(parser));
+ break;
default:
Log.e(TAG, "Unexpected tag: " + tag);
return false;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 0f15db182981..40196dbbd8b5 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -59,6 +59,7 @@ import static android.app.admin.DevicePolicyManager.DELEGATION_SECURITY_LOGGING;
import static android.app.admin.DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE_PER_USER;
import static android.app.admin.DevicePolicyManager.EXTRA_RESOURCE_ID;
import static android.app.admin.DevicePolicyManager.EXTRA_RESOURCE_TYPE_DRAWABLE;
+import static android.app.admin.DevicePolicyManager.EXTRA_RESOURCE_TYPE_STRING;
import static android.app.admin.DevicePolicyManager.ID_TYPE_BASE_INFO;
import static android.app.admin.DevicePolicyManager.ID_TYPE_IMEI;
import static android.app.admin.DevicePolicyManager.ID_TYPE_INDIVIDUAL_ATTESTATION;
@@ -99,6 +100,18 @@ import static android.app.admin.DevicePolicyManager.WIPE_EUICC;
import static android.app.admin.DevicePolicyManager.WIPE_EXTERNAL_STORAGE;
import static android.app.admin.DevicePolicyManager.WIPE_RESET_PROTECTION_DATA;
import static android.app.admin.DevicePolicyManager.WIPE_SILENTLY;
+import static android.app.admin.DevicePolicyResources.Strings.Core.LOCATION_CHANGED_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.LOCATION_CHANGED_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.NETWORK_LOGGING_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.NETWORK_LOGGING_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.NOTIFICATION_WORK_PROFILE_CONTENT_DESCRIPTION;
+import static android.app.admin.DevicePolicyResources.Strings.Core.PERSONAL_APP_SUSPENSION_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.PERSONAL_APP_SUSPENSION_TURN_ON_PROFILE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.PRINTING_DISABLED_NAMED_ADMIN;
+import static android.app.admin.DevicePolicyResources.Strings.Core.WORK_PROFILE_DELETED_FAILED_PASSWORD_ATTEMPTS_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.WORK_PROFILE_DELETED_GENERIC_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.WORK_PROFILE_DELETED_ORG_OWNED_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.Core.WORK_PROFILE_DELETED_TITLE;
import static android.app.admin.ProvisioningException.ERROR_ADMIN_PACKAGE_INSTALLATION_FAILED;
import static android.app.admin.ProvisioningException.ERROR_PRE_CONDITION_FAILED;
import static android.app.admin.ProvisioningException.ERROR_PROFILE_CREATION_FAILED;
@@ -112,6 +125,7 @@ import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_DEFAULT;
import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE;
+import static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_1;
import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
import static android.provider.Settings.Global.PRIVATE_DNS_SPECIFIER;
import static android.provider.Settings.Secure.USER_SETUP_COMPLETE;
@@ -175,6 +189,7 @@ import android.app.admin.DevicePolicyManager.PersonalAppsSuspensionReason;
import android.app.admin.DevicePolicyManagerInternal;
import android.app.admin.DevicePolicyManagerLiteInternal;
import android.app.admin.DevicePolicySafetyChecker;
+import android.app.admin.DevicePolicyStringResource;
import android.app.admin.DeviceStateCache;
import android.app.admin.FactoryResetProtectionPolicy;
import android.app.admin.FullyManagedDeviceProvisioningParams;
@@ -2234,7 +2249,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
* a managed profile.
*/
@GuardedBy("getLockObject()")
- private void applyManagedProfileRestrictionIfDeviceOwnerLocked() {
+ private void applyProfileRestrictionsIfDeviceOwnerLocked() {
final int doUserId = mOwners.getDeviceOwnerUserId();
if (doUserId == UserHandle.USER_NULL) {
if (VERBOSE_LOG) Slogf.d(LOG_TAG, "No DO found, skipping application of restriction.");
@@ -2242,7 +2257,17 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
final UserHandle doUserHandle = UserHandle.of(doUserId);
- // Set the restriction if not set.
+
+ // Based on CDD : https://source.android.com/compatibility/12/android-12-cdd#95_multi-user_support,
+ // creation of clone profile is not allowed in case device owner is set.
+ // Enforcing this restriction on setting up of device owner.
+ if (!mUserManager.hasUserRestriction(
+ UserManager.DISALLOW_ADD_CLONE_PROFILE, doUserHandle)) {
+ mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_CLONE_PROFILE, true,
+ doUserHandle);
+ }
+ // Creation of managed profile is restricted in case device owner is set, enforcing this
+ // restriction by setting user level restriction at time of device owner setup.
if (!mUserManager.hasUserRestriction(
UserManager.DISALLOW_ADD_MANAGED_PROFILE, doUserHandle)) {
mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_MANAGED_PROFILE, true,
@@ -3151,7 +3176,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
case SystemService.PHASE_ACTIVITY_MANAGER_READY:
synchronized (getLockObject()) {
migrateToProfileOnOrganizationOwnedDeviceIfCompLocked();
- applyManagedProfileRestrictionIfDeviceOwnerLocked();
+ applyProfileRestrictionsIfDeviceOwnerLocked();
}
maybeStartSecurityLogMonitorOnActivityManagerReady();
break;
@@ -3776,6 +3801,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_MANAGED_PROFILE, false,
userHandle);
}
+ // When a device owner is set, the system automatically restricts adding a clone profile.
+ // Remove this restriction when the device owner is cleared.
+ if (mUserManager.hasUserRestriction(UserManager.DISALLOW_ADD_CLONE_PROFILE, userHandle)) {
+ mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_CLONE_PROFILE, false,
+ userHandle);
+ }
}
/**
@@ -6927,12 +6958,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_WIPE_DATA);
if (TextUtils.isEmpty(wipeReasonForUser)) {
- if (calledByProfileOwnerOnOrgOwnedDevice && !calledOnParentInstance) {
- wipeReasonForUser = mContext.getString(R.string.device_ownership_relinquished);
- } else {
- wipeReasonForUser = mContext.getString(
- R.string.work_profile_deleted_description_dpm_wipe);
- }
+ wipeReasonForUser = getGenericWipeReason(
+ calledByProfileOwnerOnOrgOwnedDevice, calledOnParentInstance);
}
int userId = admin != null ? admin.getUserHandle().getIdentifier()
@@ -6983,6 +7010,18 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
wipeDataNoLock(adminComp, flags, internalReason, wipeReasonForUser, userId);
}
+ private String getGenericWipeReason(
+ boolean calledByProfileOwnerOnOrgOwnedDevice, boolean calledOnParentInstance) {
+ DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
+ return calledByProfileOwnerOnOrgOwnedDevice && !calledOnParentInstance
+ ? dpm.getString(WORK_PROFILE_DELETED_ORG_OWNED_MESSAGE,
+ () -> mContext.getString(
+ R.string.device_ownership_relinquished))
+ : dpm.getString(WORK_PROFILE_DELETED_GENERIC_MESSAGE,
+ () -> mContext.getString(
+ R.string.work_profile_deleted_description_dpm_wipe));
+ }
+
/**
* Clears device wide policies enforced by COPE PO when relinquishing the device. This method
* should be invoked once the admin is gone, so that all methods that rely on calculating
@@ -7067,7 +7106,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Notification notification =
new Notification.Builder(mContext, SystemNotificationChannels.DEVICE_ADMIN)
.setSmallIcon(android.R.drawable.stat_sys_warning)
- .setContentTitle(mContext.getString(R.string.work_profile_deleted))
+ .setContentTitle(getWorkProfileDeletedTitle())
.setContentText(wipeReasonForUser)
.setColor(mContext.getColor(R.color.system_notification_accent_color))
.setStyle(new Notification.BigTextStyle().bigText(wipeReasonForUser))
@@ -7075,6 +7114,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
mInjector.getNotificationManager().notify(SystemMessage.NOTE_PROFILE_WIPED, notification);
}
+ private String getWorkProfileDeletedTitle() {
+ DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
+ return dpm.getString(WORK_PROFILE_DELETED_TITLE,
+ () -> mContext.getString(R.string.work_profile_deleted));
+ }
+
private void clearWipeProfileNotification() {
mInjector.getNotificationManager().cancel(SystemMessage.NOTE_PROFILE_WIPED);
}
@@ -7305,12 +7350,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
// able to do so).
// IMPORTANT: Call without holding the lock to prevent deadlock.
try {
- String wipeReasonForUser = mContext.getString(
- R.string.work_profile_deleted_reason_maximum_password_failure);
wipeDataNoLock(strictestAdmin.info.getComponent(),
/*flags=*/ 0,
/*reason=*/ "reportFailedPasswordAttempt()",
- wipeReasonForUser,
+ getFailedPasswordAttemptWipeMessage(),
userId);
} catch (SecurityException e) {
Slogf.w(LOG_TAG, "Failed to wipe user " + userId
@@ -7324,6 +7367,13 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
}
+ private String getFailedPasswordAttemptWipeMessage() {
+ DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
+ return dpm.getString(WORK_PROFILE_DELETED_FAILED_PASSWORD_ATTEMPTS_MESSAGE,
+ () -> mContext.getString(
+ R.string.work_profile_deleted_reason_maximum_password_failure));
+ }
+
/**
* Returns which user should be wiped if this admin's maximum filed password attempts policy is
* violated.
@@ -8468,6 +8518,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
// on the primary profile).
mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_MANAGED_PROFILE, true,
UserHandle.of(userId));
+ // Restrict adding a clone profile when a device owner is set on the device.
+ // That is to prevent the co-existence of a clone profile and a device owner
+ // on the same device.
+ // CDD for reference : https://source.android.com/compatibility/12/android-12-cdd#95_multi-user_support
+ mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_CLONE_PROFILE, true,
+ UserHandle.of(userId));
// TODO Send to system too?
sendOwnerChangedBroadcast(DevicePolicyManager.ACTION_DEVICE_OWNER_CHANGED, userId);
});
@@ -8940,7 +8996,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
mOwners.writeProfileOwner(userId);
deleteTransferOwnershipBundleLocked(userId);
toggleBackupServiceActive(userId, true);
- applyManagedProfileRestrictionIfDeviceOwnerLocked();
+ applyProfileRestrictionsIfDeviceOwnerLocked();
setNetworkLoggingActiveInternal(false);
}
@@ -12395,8 +12451,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Notification notification = new Notification.Builder(mContext,
SystemNotificationChannels.DEVICE_ADMIN)
.setSmallIcon(R.drawable.ic_info_outline)
- .setContentTitle(mContext.getString(R.string.location_changed_notification_title))
- .setContentText(mContext.getString(R.string.location_changed_notification_text))
+ .setContentTitle(getLocationChangedTitle())
+ .setContentText(getLocationChangedText())
.setColor(mContext.getColor(R.color.system_notification_accent_color))
.setShowWhen(true)
.setContentIntent(locationSettingsIntent)
@@ -12406,6 +12462,18 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
notification);
}
+ private String getLocationChangedTitle() {
+ DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
+ return dpm.getString(LOCATION_CHANGED_TITLE,
+ () -> mContext.getString(R.string.location_changed_notification_title));
+ }
+
+ private String getLocationChangedText() {
+ DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
+ return dpm.getString(LOCATION_CHANGED_MESSAGE,
+ () -> mContext.getString(R.string.location_changed_notification_text));
+ }
+
@Override
public boolean setTime(ComponentName who, long millis) {
Objects.requireNonNull(who, "ComponentName is null");
@@ -12996,11 +13064,19 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Slogf.e(LOG_TAG, "appLabel is inexplicably null");
return null;
}
- return ((Context) ActivityThread.currentActivityThread().getSystemUiContext())
- .getResources().getString(R.string.printing_disabled_by, appLabel);
+ DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
+ return dpm.getString(
+ PRINTING_DISABLED_NAMED_ADMIN,
+ () -> getDefaultPrintingDisabledMsg(appLabel),
+ appLabel);
}
}
+ private String getDefaultPrintingDisabledMsg(CharSequence appLabel) {
+ return ((Context) ActivityThread.currentActivityThread().getSystemUiContext())
+ .getResources().getString(R.string.printing_disabled_by, appLabel);
+ }
+
@Override
protected DevicePolicyCache getDevicePolicyCache() {
return mPolicyCache;
@@ -15532,16 +15608,18 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
// Simple notification clicks are immutable
final PendingIntent pendingIntent = PendingIntent.getBroadcastAsUser(mContext, 0, intent,
PendingIntent.FLAG_IMMUTABLE, UserHandle.CURRENT);
+
+ final String title = getNetworkLoggingTitle();
+ final String text = getNetworkLoggingText();
Notification notification =
new Notification.Builder(mContext, SystemNotificationChannels.DEVICE_ADMIN)
.setSmallIcon(R.drawable.ic_info_outline)
- .setContentTitle(mContext.getString(R.string.network_logging_notification_title))
- .setContentText(mContext.getString(R.string.network_logging_notification_text))
- .setTicker(mContext.getString(R.string.network_logging_notification_title))
+ .setContentTitle(title)
+ .setContentText(text)
+ .setTicker(title)
.setShowWhen(true)
.setContentIntent(pendingIntent)
- .setStyle(new Notification.BigTextStyle()
- .bigText(mContext.getString(R.string.network_logging_notification_text)))
+ .setStyle(new Notification.BigTextStyle().bigText(text))
.build();
Slogf.i(LOG_TAG, "Sending network logging notification to user %d",
mNetworkLoggingNotificationUserId);
@@ -15550,6 +15628,18 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
UserHandle.of(mNetworkLoggingNotificationUserId));
}
+ private String getNetworkLoggingTitle() {
+ DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
+ return dpm.getString(NETWORK_LOGGING_TITLE,
+ () -> mContext.getString(R.string.network_logging_notification_title));
+ }
+
+ private String getNetworkLoggingText() {
+ DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
+ return dpm.getString(NETWORK_LOGGING_MESSAGE,
+ () -> mContext.getString(R.string.network_logging_notification_text));
+ }
+
private void handleCancelNetworkLoggingNotification() {
if (mNetworkLoggingNotificationUserId == UserHandle.USER_NULL) {
// Happens when setNetworkLoggingActive(false) is called before called with true
@@ -17042,10 +17132,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
0 /* requestCode */, intent,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
- final String buttonText =
- mContext.getString(R.string.personal_apps_suspended_turn_profile_on);
- final Notification.Action turnProfileOnButton =
- new Notification.Action.Builder(null /* icon */, buttonText, pendingIntent).build();
+ final Notification.Action turnProfileOnButton = new Notification.Action.Builder(
+ /* icon= */ null, getPersonalAppSuspensionButtonText(), pendingIntent).build();
final String text;
final boolean ongoing;
@@ -17057,26 +17145,24 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
mContext, profileOwner.mProfileOffDeadline, DateUtils.FORMAT_SHOW_DATE);
final String time = DateUtils.formatDateTime(
mContext, profileOwner.mProfileOffDeadline, DateUtils.FORMAT_SHOW_TIME);
- text = mContext.getString(
- R.string.personal_apps_suspension_soon_text, date, time, maxDays);
+ text = getPersonalAppSuspensionSoonText(date, time, maxDays);
ongoing = false;
} else {
- text = mContext.getString(R.string.personal_apps_suspension_text);
+ text = getPersonalAppSuspensionText();
ongoing = true;
}
final int color = mContext.getColor(R.color.personal_apps_suspension_notification_color);
final Bundle extras = new Bundle();
// TODO: Create a separate string for this.
- extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME,
- mContext.getString(R.string.notification_work_profile_content_description));
+ extras.putString(
+ Notification.EXTRA_SUBSTITUTE_APP_NAME, getWorkProfileContentDescription());
final Notification notification =
new Notification.Builder(mContext, SystemNotificationChannels.DEVICE_ADMIN)
.setSmallIcon(R.drawable.ic_corp_badge_no_background)
.setOngoing(ongoing)
.setAutoCancel(false)
- .setContentTitle(mContext.getString(
- R.string.personal_apps_suspension_title))
+ .setContentTitle(getPersonalAppSuspensionTitle())
.setContentText(text)
.setStyle(new Notification.BigTextStyle().bigText(text))
.setColor(color)
@@ -17087,6 +17173,38 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
SystemMessage.NOTE_PERSONAL_APPS_SUSPENDED, notification);
}
+ private String getPersonalAppSuspensionButtonText() {
+ DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
+ return dpm.getString(PERSONAL_APP_SUSPENSION_TURN_ON_PROFILE,
+ () -> mContext.getString(R.string.personal_apps_suspended_turn_profile_on));
+ }
+
+ private String getPersonalAppSuspensionTitle() {
+ DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
+ return dpm.getString(PERSONAL_APP_SUSPENSION_TITLE,
+ () -> mContext.getString(R.string.personal_apps_suspension_title));
+ }
+
+ private String getPersonalAppSuspensionText() {
+ DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
+ return dpm.getString(PERSONAL_APP_SUSPENSION_TITLE,
+ () -> mContext.getString(R.string.personal_apps_suspension_text));
+ }
+
+ private String getPersonalAppSuspensionSoonText(String date, String time, int maxDays) {
+ DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
+ return dpm.getString(PERSONAL_APP_SUSPENSION_TITLE,
+ () -> mContext.getString(
+ R.string.personal_apps_suspension_soon_text, date, time, maxDays),
+ date, time, maxDays);
+ }
+
+ private String getWorkProfileContentDescription() {
+ DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
+ return dpm.getString(NOTIFICATION_WORK_PROFILE_CONTENT_DESCRIPTION,
+ () -> mContext.getString(R.string.notification_work_profile_content_description));
+ }
+
@Override
public void setManagedProfileMaximumTimeOff(ComponentName who, long timeoutMillis) {
Objects.requireNonNull(who, "ComponentName is null");
@@ -17850,7 +17968,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
? PROFILE_NETWORK_PREFERENCE_ENTERPRISE : PROFILE_NETWORK_PREFERENCE_DEFAULT;
ProfileNetworkPreference.Builder preferenceBuilder =
new ProfileNetworkPreference.Builder();
- preferenceBuilder.setPreference(networkPreference);
+ if (preferentialNetworkServiceEnabled) {
+ preferenceBuilder.setPreference(PROFILE_NETWORK_PREFERENCE_ENTERPRISE);
+ preferenceBuilder.setPreferenceEnterpriseId(NET_ENTERPRISE_ID_1);
+ } else {
+ preferenceBuilder.setPreference(PROFILE_NETWORK_PREFERENCE_DEFAULT);
+ }
List<ProfileNetworkPreference> preferences = new ArrayList<>();
preferences.add(preferenceBuilder.build());
mInjector.binderWithCleanCallingIdentity(() ->
@@ -17977,6 +18100,117 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
);
}
+ private void validateCurrentWifiMeetsAdminRequirements() {
+ mInjector.binderWithCleanCallingIdentity(
+ () -> mInjector.getWifiManager().validateCurrentWifiMeetsAdminRequirements());
+ }
+
+ @Override
+ public void setMinimumRequiredWifiSecurityLevel(int level) {
+ final CallerIdentity caller = getCallerIdentity();
+ Preconditions.checkCallAuthorization(
+ isDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller),
+ "Wi-Fi minimum security level can only be controlled by a device owner or "
+ + "a profile owner on an organization-owned device.");
+
+ boolean valueChanged = false;
+ synchronized (getLockObject()) {
+ final ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller);
+ if (admin.mWifiMinimumSecurityLevel != level) {
+ admin.mWifiMinimumSecurityLevel = level;
+ saveSettingsLocked(caller.getUserId());
+ valueChanged = true;
+ }
+ }
+ if (valueChanged) validateCurrentWifiMeetsAdminRequirements();
+ }
+
+ @Override
+ public int getMinimumRequiredWifiSecurityLevel() {
+ synchronized (getLockObject()) {
+ final ActiveAdmin admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked(
+ UserHandle.USER_SYSTEM);
+ return (admin == null) ? DevicePolicyManager.WIFI_SECURITY_OPEN
+ : admin.mWifiMinimumSecurityLevel;
+ }
+ }
+
+ @Override
+ public void setSsidAllowlist(List<String> ssids) {
+ final CallerIdentity caller = getCallerIdentity();
+ Preconditions.checkCallAuthorization(
+ isDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller),
+ "SSID allowlist can only be controlled by a device owner or "
+ + "a profile owner on an organization-owned device.");
+
+ Collections.sort(ssids);
+ boolean changed = false;
+ synchronized (getLockObject()) {
+ final ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller);
+ if (!ssids.equals(admin.mSsidAllowlist)) {
+ admin.mSsidAllowlist = ssids;
+ admin.mSsidDenylist = null;
+ changed = true;
+ }
+ if (changed) saveSettingsLocked(caller.getUserId());
+ }
+ if (changed) validateCurrentWifiMeetsAdminRequirements();
+ }
+
+ @Override
+ public List<String> getSsidAllowlist() {
+ final CallerIdentity caller = getCallerIdentity();
+ Preconditions.checkCallAuthorization(
+ isDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller)
+ || isSystemUid(caller),
+ "SSID allowlist can only be retrieved by a device owner or "
+ + "a profile owner on an organization-owned device or a system app.");
+ synchronized (getLockObject()) {
+ final ActiveAdmin admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked(
+ UserHandle.USER_SYSTEM);
+ return (admin == null || admin.mSsidAllowlist == null) ? new ArrayList<>()
+ : admin.mSsidAllowlist;
+ }
+ }
+
+ @Override
+ public void setSsidDenylist(List<String> ssids) {
+ final CallerIdentity caller = getCallerIdentity();
+ Preconditions.checkCallAuthorization(
+ isDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller),
+ "SSID denylist can only be controlled by a device owner or "
+ + "a profile owner on an organization-owned device.");
+
+ Collections.sort(ssids);
+ boolean changed = false;
+ synchronized (getLockObject()) {
+ final ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller);
+ if (!ssids.equals(admin.mSsidDenylist)) {
+ admin.mSsidDenylist = ssids;
+ admin.mSsidAllowlist = null;
+ changed = true;
+ }
+ if (changed) saveSettingsLocked(caller.getUserId());
+ }
+ if (changed) validateCurrentWifiMeetsAdminRequirements();
+ }
+
+ @Override
+ public List<String> getSsidDenylist() {
+ final CallerIdentity caller = getCallerIdentity();
+ Preconditions.checkCallAuthorization(
+ isDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller)
+ || isSystemUid(caller),
+ "SSID denylist can only be retrieved by a device owner or "
+ + "a profile owner on an organization-owned device or a system app.");
+ synchronized (getLockObject()) {
+ final ActiveAdmin admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked(
+ UserHandle.USER_SYSTEM);
+ return (admin == null || admin.mSsidDenylist == null) ? new ArrayList<>()
+ : admin.mSsidDenylist;
+ }
+ }
+
@Override
public void setDrawables(@NonNull List<DevicePolicyDrawableResource> drawables) {
Preconditions.checkCallAuthorization(hasCallingOrSelfPermission(
@@ -18026,4 +18260,50 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
mContext.sendBroadcastAsUser(intent, user);
}
}
+
+ @Override
+ public void setStrings(@NonNull List<DevicePolicyStringResource> strings) {
+ Preconditions.checkCallAuthorization(hasCallingOrSelfPermission(
+ android.Manifest.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES));
+
+ Objects.requireNonNull(strings, "strings must be provided.");
+
+ mInjector.binderWithCleanCallingIdentity(() -> {
+ if (mDeviceManagementResourcesProvider.updateStrings(strings))
+ sendStringsUpdatedBroadcast(
+ strings.stream().map(s -> s.getStringId()).toArray(String[]::new));
+ });
+ }
+
+ @Override
+ public void resetStrings(String[] stringIds) {
+ Preconditions.checkCallAuthorization(hasCallingOrSelfPermission(
+ android.Manifest.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES));
+
+ mInjector.binderWithCleanCallingIdentity(() -> {
+ if (mDeviceManagementResourcesProvider.removeStrings(stringIds)) {
+ sendStringsUpdatedBroadcast(stringIds);
+ }
+ });
+ }
+
+ @Override
+ public ParcelableResource getString(String stringId) {
+ return mInjector.binderWithCleanCallingIdentity(() ->
+ mDeviceManagementResourcesProvider.getString(stringId));
+ }
+
+ private void sendStringsUpdatedBroadcast(String[] stringIds) {
+ final Intent intent = new Intent(ACTION_DEVICE_POLICY_RESOURCE_UPDATED);
+ intent.putExtra(EXTRA_RESOURCE_ID, stringIds);
+ intent.putExtra(EXTRA_RESOURCE_TYPE_STRING, /* value= */ true);
+ intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+
+ List<UserInfo> users = mUserManager.getAliveUsers();
+ for (int i = 0; i < users.size(); i++) {
+ UserHandle user = users.get(i).getUserHandle();
+ mContext.sendBroadcastAsUser(intent, user);
+ }
+ }
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 4b21454bdc00..1fe71f8e13a1 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -54,6 +54,7 @@ import android.hardware.display.DisplayManagerInternal;
import android.net.ConnectivityManager;
import android.net.ConnectivityModuleConnector;
import android.net.NetworkStackClient;
+import android.net.TrafficStats;
import android.os.BaseBundle;
import android.os.Binder;
import android.os.Build;
@@ -103,6 +104,7 @@ import com.android.internal.util.EmergencyAffordanceManager;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.widget.ILockSettings;
import com.android.server.am.ActivityManagerService;
+import com.android.server.ambientcontext.AmbientContextManagerService;
import com.android.server.appbinding.AppBindingService;
import com.android.server.art.ArtManagerLocal;
import com.android.server.attention.AttentionManagerService;
@@ -136,6 +138,7 @@ import com.android.server.integrity.AppIntegrityManagerService;
import com.android.server.lights.LightsService;
import com.android.server.locales.LocaleManagerService;
import com.android.server.location.LocationManagerService;
+import com.android.server.logcat.LogcatManagerService;
import com.android.server.media.MediaRouterService;
import com.android.server.media.metrics.MediaMetricsManagerService;
import com.android.server.media.projection.MediaProjectionManagerService;
@@ -194,7 +197,7 @@ import com.android.server.tracing.TracingServiceProxy;
import com.android.server.trust.TrustManagerService;
import com.android.server.tv.TvInputManagerService;
import com.android.server.tv.TvRemoteService;
-import com.android.server.tv.interactive.TvIAppManagerService;
+import com.android.server.tv.interactive.TvInteractiveAppManagerService;
import com.android.server.tv.tunerresourcemanager.TunerResourceManagerService;
import com.android.server.twilight.TwilightService;
import com.android.server.uri.UriGrantsManagerService;
@@ -261,6 +264,8 @@ public final class SystemServer implements Dumpable {
"/apex/com.android.os.statsd/javalib/service-statsd.jar";
private static final String CONNECTIVITY_SERVICE_APEX_PATH =
"/apex/com.android.tethering/javalib/service-connectivity.jar";
+ private static final String NEARBY_SERVICE_APEX_PATH =
+ "/apex/com.android.nearby/javalib/service-nearby.jar";
private static final String STATS_COMPANION_LIFECYCLE_CLASS =
"com.android.server.stats.StatsCompanion$Lifecycle";
private static final String STATS_PULL_ATOM_SERVICE_CLASS =
@@ -271,6 +276,8 @@ public final class SystemServer implements Dumpable {
"com.android.server.usb.UsbService$Lifecycle";
private static final String MIDI_SERVICE_CLASS =
"com.android.server.midi.MidiService$Lifecycle";
+ private static final String NEARBY_SERVICE_CLASS =
+ "com.android.server.nearby.NearbyService";
private static final String WIFI_APEX_SERVICE_JAR_PATH =
"/apex/com.android.wifi/javalib/service-wifi.jar";
private static final String WIFI_SERVICE_CLASS =
@@ -403,8 +410,6 @@ public final class SystemServer implements Dumpable {
private static final String SAFETY_CENTER_SERVICE_CLASS =
"com.android.safetycenter.SafetyCenterService";
- private static final String SUPPLEMENTALPROCESS_APEX_PATH =
- "/apex/com.android.supplementalprocess/javalib/service-supplementalprocess.jar";
private static final String SUPPLEMENTALPROCESS_SERVICE_CLASS =
"com.android.server.supplementalprocess.SupplementalProcessManagerService$Lifecycle";
@@ -1627,6 +1632,10 @@ public final class SystemServer implements Dumpable {
mSystemServiceManager.startService(AppIntegrityManagerService.class);
t.traceEnd();
+ t.traceBegin("StartLogcatManager");
+ mSystemServiceManager.startService(LogcatManagerService.class);
+ t.traceEnd();
+
} catch (Throwable e) {
Slog.e("System", "******************************************");
Slog.e("System", "************ Failure starting core service");
@@ -1807,6 +1816,7 @@ public final class SystemServer implements Dumpable {
startRotationResolverService(context, t);
startSystemCaptionsManagerService(context, t);
startTextToSpeechManagerService(context, t);
+ startAmbientContextService(t);
// System Speech Recognition Service
t.traceBegin("StartSpeechRecognitionManagerService");
@@ -1901,6 +1911,7 @@ public final class SystemServer implements Dumpable {
try {
networkStats = NetworkStatsService.create(context);
ServiceManager.addService(Context.NETWORK_STATS_SERVICE, networkStats);
+ TrafficStats.init(context);
} catch (Throwable e) {
reportWtf("starting NetworkStats Service", e);
}
@@ -1976,6 +1987,16 @@ public final class SystemServer implements Dumpable {
}
t.traceEnd();
+ // Start Nearby Service.
+ t.traceBegin("StartNearbyService");
+ try {
+ mSystemServiceManager.startServiceFromJar(NEARBY_SERVICE_CLASS,
+ NEARBY_SERVICE_APEX_PATH);
+ } catch (Throwable e) {
+ reportWtf("starting NearbyService", e);
+ }
+ t.traceEnd();
+
t.traceBegin("StartConnectivityService");
// This has to be called after NetworkManagementService, NetworkStatsService
// and NetworkPolicyManager because ConnectivityService needs to take these
@@ -2367,8 +2388,8 @@ public final class SystemServer implements Dumpable {
if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_LIVE_TV)
|| mPackageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)) {
- t.traceBegin("StartTvIAppManager");
- mSystemServiceManager.startService(TvIAppManagerService.class);
+ t.traceBegin("StartTvInteractiveAppManager");
+ mSystemServiceManager.startService(TvInteractiveAppManagerService.class);
t.traceEnd();
}
@@ -2558,8 +2579,7 @@ public final class SystemServer implements Dumpable {
// Supplemental Process
t.traceBegin("StartSupplementalProcessManagerService");
- mSystemServiceManager.startServiceFromJar(SUPPLEMENTALPROCESS_SERVICE_CLASS,
- SUPPLEMENTALPROCESS_APEX_PATH);
+ mSystemServiceManager.startService(SUPPLEMENTALPROCESS_SERVICE_CLASS);
t.traceEnd();
if (safeMode) {
@@ -3149,6 +3169,17 @@ public final class SystemServer implements Dumpable {
}
+ private void startAmbientContextService(@NonNull TimingsTraceAndSlog t) {
+ if (!AmbientContextManagerService.isDetectionServiceConfigured()) {
+ Slog.d(TAG, "AmbientContextDetectionService is not configured on this device");
+ return;
+ }
+
+ t.traceBegin("StartAmbientContextService");
+ mSystemServiceManager.startService(AmbientContextManagerService.class);
+ t.traceEnd();
+ }
+
private static void startSystemUi(Context context, WindowManagerService windowManager) {
PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class);
Intent intent = new Intent();
diff --git a/services/midi/java/com/android/server/midi/MidiService.java b/services/midi/java/com/android/server/midi/MidiService.java
index ca31efcdf3d2..d56278629bf2 100644
--- a/services/midi/java/com/android/server/midi/MidiService.java
+++ b/services/midi/java/com/android/server/midi/MidiService.java
@@ -18,9 +18,11 @@ package com.android.server.midi;
import android.annotation.NonNull;
import android.bluetooth.BluetoothDevice;
+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.PackageInfo;
@@ -33,6 +35,7 @@ import android.media.midi.IMidiDeviceListener;
import android.media.midi.IMidiDeviceOpenCallback;
import android.media.midi.IMidiDeviceServer;
import android.media.midi.IMidiManager;
+import android.media.midi.MidiDevice;
import android.media.midi.MidiDeviceInfo;
import android.media.midi.MidiDeviceService;
import android.media.midi.MidiDeviceStatus;
@@ -55,6 +58,7 @@ import com.android.server.SystemService.TargetUser;
import org.xmlpull.v1.XmlPullParser;
import java.io.FileDescriptor;
+import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
@@ -96,9 +100,12 @@ public class MidiService extends IMidiManager.Stub {
= new HashMap<MidiDeviceInfo, Device>();
// list of all Bluetooth devices, keyed by BluetoothDevice
- private final HashMap<BluetoothDevice, Device> mBluetoothDevices
+ private final HashMap<BluetoothDevice, Device> mBluetoothDevices
= new HashMap<BluetoothDevice, Device>();
+ private final HashMap<BluetoothDevice, MidiDevice> mBleMidiDeviceMap =
+ new HashMap<BluetoothDevice, MidiDevice>();
+
// list of all devices, keyed by IMidiDeviceServer
private final HashMap<IBinder, Device> mDevicesByServer = new HashMap<IBinder, Device>();
@@ -569,10 +576,45 @@ public class MidiService extends IMidiManager.Stub {
}
}
+ private final BroadcastReceiver mBleMidiReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (action == null) {
+ Log.w(TAG, "MidiService, action is null");
+ return;
+ }
+
+ switch (action) {
+ case BluetoothDevice.ACTION_ACL_CONNECTED: {
+ Log.d(TAG, "ACTION_ACL_CONNECTED");
+ BluetoothDevice btDevice =
+ intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+ openBluetoothDevice(btDevice);
+ }
+ break;
+
+ case BluetoothDevice.ACTION_ACL_DISCONNECTED: {
+ Log.d(TAG, "ACTION_ACL_DISCONNECTED");
+ BluetoothDevice btDevice =
+ intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+ closeBluetoothDevice(btDevice);
+ }
+ break;
+ }
+ }
+ };
+
public MidiService(Context context) {
mContext = context;
mPackageManager = context.getPackageManager();
+ // Setup broadcast receivers
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
+ filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
+ context.registerReceiver(mBleMidiReceiver, filter);
+
mBluetoothServiceUid = -1;
}
@@ -643,13 +685,31 @@ public class MidiService extends IMidiManager.Stub {
private static final MidiDeviceInfo[] EMPTY_DEVICE_INFO_ARRAY = new MidiDeviceInfo[0];
public MidiDeviceInfo[] getDevices() {
+ return getDevicesForTransport(MidiManager.TRANSPORT_MIDI_BYTE_STREAM);
+ }
+
+ /**
+ * @hide
+ */
+ public MidiDeviceInfo[] getDevicesForTransport(int transport) {
ArrayList<MidiDeviceInfo> deviceInfos = new ArrayList<MidiDeviceInfo>();
int uid = Binder.getCallingUid();
synchronized (mDevicesByInfo) {
for (Device device : mDevicesByInfo.values()) {
if (device.isUidAllowed(uid)) {
- deviceInfos.add(device.getDeviceInfo());
+ // UMP devices have protocols that are not PROTOCOL_UNKNOWN
+ if (transport == MidiManager.TRANSPORT_UNIVERSAL_MIDI_PACKETS) {
+ if (device.getDeviceInfo().getDefaultProtocol()
+ != MidiDeviceInfo.PROTOCOL_UNKNOWN) {
+ deviceInfos.add(device.getDeviceInfo());
+ }
+ } else if (transport == MidiManager.TRANSPORT_MIDI_BYTE_STREAM) {
+ if (device.getDeviceInfo().getDefaultProtocol()
+ == MidiDeviceInfo.PROTOCOL_UNKNOWN) {
+ deviceInfos.add(device.getDeviceInfo());
+ }
+ }
}
}
}
@@ -683,9 +743,43 @@ public class MidiService extends IMidiManager.Stub {
}
}
+ private void openBluetoothDevice(BluetoothDevice bluetoothDevice) {
+ Log.d(TAG, "openBluetoothDevice() device: " + bluetoothDevice);
+
+ MidiManager midiManager = mContext.getSystemService(MidiManager.class);
+ midiManager.openBluetoothDevice(bluetoothDevice,
+ new MidiManager.OnDeviceOpenedListener() {
+ @Override
+ public void onDeviceOpened(MidiDevice device) {
+ synchronized (mBleMidiDeviceMap) {
+ mBleMidiDeviceMap.put(bluetoothDevice, device);
+ }
+ }
+ }, null);
+ }
+
+ private void closeBluetoothDevice(BluetoothDevice bluetoothDevice) {
+ Log.d(TAG, "closeBluetoothDevice() device: " + bluetoothDevice);
+
+ MidiDevice midiDevice;
+ synchronized (mBleMidiDeviceMap) {
+ midiDevice = mBleMidiDeviceMap.remove(bluetoothDevice);
+ }
+
+ if (midiDevice != null) {
+ try {
+ midiDevice.close();
+ } catch (IOException ex) {
+ Log.e(TAG, "Exception closing BLE-MIDI device" + ex);
+ }
+ }
+ }
+
@Override
public void openBluetoothDevice(IBinder token, BluetoothDevice bluetoothDevice,
IMidiDeviceOpenCallback callback) {
+ Log.d(TAG, "openBluetoothDevice()");
+
Client client = getClient(token);
if (client == null) return;
@@ -718,7 +812,7 @@ public class MidiService extends IMidiManager.Stub {
@Override
public MidiDeviceInfo registerDeviceServer(IMidiDeviceServer server, int numInputPorts,
int numOutputPorts, String[] inputPortNames, String[] outputPortNames,
- Bundle properties, int type) {
+ Bundle properties, int type, int defaultProtocol) {
int uid = Binder.getCallingUid();
if (type == MidiDeviceInfo.TYPE_USB && uid != Process.SYSTEM_UID) {
throw new SecurityException("only system can create USB devices");
@@ -728,7 +822,8 @@ public class MidiService extends IMidiManager.Stub {
synchronized (mDevicesByInfo) {
return addDeviceLocked(type, numInputPorts, numOutputPorts, inputPortNames,
- outputPortNames, properties, server, null, false, uid);
+ outputPortNames, properties, server, null, false, uid,
+ defaultProtocol);
}
}
@@ -769,7 +864,15 @@ public class MidiService extends IMidiManager.Stub {
if (device == null) {
throw new IllegalArgumentException("no such device for " + deviceInfo);
}
- return device.getDeviceStatus();
+ int uid = Binder.getCallingUid();
+ if (device.isUidAllowed(uid)) {
+ return device.getDeviceStatus();
+ } else {
+ Log.e(TAG, "getDeviceStatus() invalid UID = " + uid);
+ EventLog.writeEvent(0x534e4554, "203549963",
+ uid, "getDeviceStatus: invalid uid");
+ return null;
+ }
}
@Override
@@ -797,11 +900,12 @@ public class MidiService extends IMidiManager.Stub {
private MidiDeviceInfo addDeviceLocked(int type, int numInputPorts, int numOutputPorts,
String[] inputPortNames, String[] outputPortNames, Bundle properties,
IMidiDeviceServer server, ServiceInfo serviceInfo,
- boolean isPrivate, int uid) {
+ boolean isPrivate, int uid, int defaultProtocol) {
int id = mNextDeviceId++;
MidiDeviceInfo deviceInfo = new MidiDeviceInfo(type, id, numInputPorts, numOutputPorts,
- inputPortNames, outputPortNames, properties, isPrivate);
+ inputPortNames, outputPortNames, properties, isPrivate,
+ defaultProtocol);
if (server != null) {
try {
@@ -988,10 +1092,11 @@ public class MidiService extends IMidiManager.Stub {
synchronized (mDevicesByInfo) {
addDeviceLocked(MidiDeviceInfo.TYPE_VIRTUAL,
- numInputPorts, numOutputPorts,
- inputPortNames.toArray(EMPTY_STRING_ARRAY),
- outputPortNames.toArray(EMPTY_STRING_ARRAY),
- properties, null, serviceInfo, isPrivate, uid);
+ numInputPorts, numOutputPorts,
+ inputPortNames.toArray(EMPTY_STRING_ARRAY),
+ outputPortNames.toArray(EMPTY_STRING_ARRAY),
+ properties, null, serviceInfo, isPrivate, uid,
+ MidiDeviceInfo.PROTOCOL_UNKNOWN);
}
// setting properties to null signals that we are no longer
// processing a <device>
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
index a0f3bbf928ab..cc663d955612 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
@@ -503,6 +503,11 @@ class AndroidPackageTest : ParcelableComponentTest(AndroidPackage::class, Packag
PackageManager.Property::getString
)
}
+ ),
+ getSetByValue(
+ AndroidPackage::shouldInheritKeyStoreKeys,
+ ParsingPackage::setInheritKeyStoreKeys,
+ true
)
)
diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp
index 635f1360ff73..36246e5f3857 100644
--- a/services/tests/mockingservicestests/Android.bp
+++ b/services/tests/mockingservicestests/Android.bp
@@ -14,7 +14,7 @@
java_defaults {
name: "FrameworkMockingServicesTests-jni-defaults",
jni_libs: [
- "libactivitymanagermockingservicestestjni",
+ "libmockingservicestestjni",
],
}
diff --git a/services/tests/mockingservicestests/jni/Android.bp b/services/tests/mockingservicestests/jni/Android.bp
index a32bf2c3b260..89b204b9c999 100644
--- a/services/tests/mockingservicestests/jni/Android.bp
+++ b/services/tests/mockingservicestests/jni/Android.bp
@@ -8,7 +8,7 @@ package {
}
cc_library_shared {
- name: "libactivitymanagermockingservicestestjni",
+ name: "libmockingservicestestjni",
cflags: [
"-Wall",
@@ -19,12 +19,15 @@ cc_library_shared {
srcs: [
":lib_cachedAppOptimizer_native",
+ ":lib_gameManagerService_native",
"onload.cpp",
],
include_dirs: [
"frameworks/base/libs",
"frameworks/native/services",
+ "frameworks/native/libs/math/include",
+ "frameworks/native/libs/ui/include",
"system/memory/libmeminfo/include",
],
@@ -33,10 +36,19 @@ cc_library_shared {
"libandroid_runtime",
"libbase",
"libbinder",
+ "libgralloctypes",
+ "libgui",
+ "libhidlbase",
"liblog",
"libmeminfo",
"libnativehelper",
"libprocessgroup",
"libutils",
+ "android.hardware.graphics.bufferqueue@1.0",
+ "android.hardware.graphics.bufferqueue@2.0",
+ "android.hardware.graphics.common@1.2",
+ "android.hardware.graphics.common-V3-ndk",
+ "android.hardware.graphics.mapper@4.0",
+ "android.hidl.token@1.0-utils",
],
}
diff --git a/services/tests/mockingservicestests/jni/onload.cpp b/services/tests/mockingservicestests/jni/onload.cpp
index 147cc479be18..23ccb22321b2 100644
--- a/services/tests/mockingservicestests/jni/onload.cpp
+++ b/services/tests/mockingservicestests/jni/onload.cpp
@@ -25,6 +25,7 @@
namespace android {
int register_android_server_am_CachedAppOptimizer(JNIEnv* env);
+int register_android_server_app_GameManagerService(JNIEnv* env);
};
using namespace android;
@@ -40,6 +41,7 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
}
ALOG_ASSERT(env, "Could not retrieve the env!");
register_android_server_am_CachedAppOptimizer(env);
+ register_android_server_app_GameManagerService(env);
return JNI_VERSION_1_4;
}
diff --git a/services/tests/mockingservicestests/src/android/service/games/GameSessionTest.java b/services/tests/mockingservicestests/src/android/service/games/GameSessionTest.java
new file mode 100644
index 000000000000..fec9b1249d17
--- /dev/null
+++ b/services/tests/mockingservicestests/src/android/service/games/GameSessionTest.java
@@ -0,0 +1,391 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.games;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+
+import android.graphics.Bitmap;
+import android.platform.test.annotations.Presubmit;
+import android.service.games.GameSession.ScreenshotCallback;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.SurfaceControlViewHost;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.infra.AndroidFuture;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoSession;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Unit tests for the {@link android.service.games.GameSession}.
+ */
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+@Presubmit
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+public final class GameSessionTest {
+ private static final long WAIT_FOR_CALLBACK_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(1);
+ private static final Bitmap TEST_BITMAP = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
+
+ @Mock
+ private IGameSessionController mMockGameSessionController;
+ @Mock
+ SurfaceControlViewHost mSurfaceControlViewHost;
+ private LifecycleTrackingGameSession mGameSession;
+ private MockitoSession mMockitoSession;
+
+ @Before
+ public void setUp() {
+ mMockitoSession = mockitoSession()
+ .initMocks(this)
+ .startMocking();
+
+ mGameSession = new LifecycleTrackingGameSession() {};
+ mGameSession.attach(mMockGameSessionController, /* taskId= */ 10,
+ InstrumentationRegistry.getContext(),
+ mSurfaceControlViewHost,
+ /* widthPx= */ 0, /* heightPx= */0);
+ }
+
+ @After
+ public void tearDown() {
+ mMockitoSession.finishMocking();
+ }
+
+ @Test
+ public void takeScreenshot_attachNotCalled_throwsIllegalStateException() throws Exception {
+ GameSession gameSession = new GameSession() {};
+
+ try {
+ gameSession.takeScreenshot(DIRECT_EXECUTOR,
+ new ScreenshotCallback() {
+ @Override
+ public void onFailure(int statusCode) {
+ fail();
+ }
+
+ @Override
+ public void onSuccess(Bitmap bitmap) {
+ fail();
+ }
+ });
+ fail();
+ } catch (IllegalStateException expected) {
+
+ }
+ }
+
+ @Test
+ public void takeScreenshot_gameManagerException_returnsInternalError() throws Exception {
+ doAnswer(invocation -> {
+ AndroidFuture result = invocation.getArgument(1);
+ result.completeExceptionally(new Exception());
+ return null;
+ }).when(mMockGameSessionController).takeScreenshot(anyInt(), any());
+
+ CountDownLatch countDownLatch = new CountDownLatch(1);
+
+ mGameSession.takeScreenshot(DIRECT_EXECUTOR,
+ new ScreenshotCallback() {
+ @Override
+ public void onFailure(int statusCode) {
+ assertEquals(ScreenshotCallback.ERROR_TAKE_SCREENSHOT_INTERNAL_ERROR,
+ statusCode);
+ countDownLatch.countDown();
+ }
+
+ @Override
+ public void onSuccess(Bitmap bitmap) {
+ fail();
+ }
+ });
+
+ assertTrue(countDownLatch.await(
+ WAIT_FOR_CALLBACK_TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ }
+
+ @Test
+ public void takeScreenshot_gameManagerError_returnsInternalError() throws Exception {
+ doAnswer(invocation -> {
+ AndroidFuture result = invocation.getArgument(1);
+ result.complete(GameScreenshotResult.createInternalErrorResult());
+ return null;
+ }).when(mMockGameSessionController).takeScreenshot(anyInt(), any());
+
+ CountDownLatch countDownLatch = new CountDownLatch(1);
+
+ mGameSession.takeScreenshot(DIRECT_EXECUTOR,
+ new ScreenshotCallback() {
+ @Override
+ public void onFailure(int statusCode) {
+ assertEquals(ScreenshotCallback.ERROR_TAKE_SCREENSHOT_INTERNAL_ERROR,
+ statusCode);
+ countDownLatch.countDown();
+ }
+
+ @Override
+ public void onSuccess(Bitmap bitmap) {
+ fail();
+ }
+ });
+
+ assertTrue(countDownLatch.await(
+ WAIT_FOR_CALLBACK_TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ }
+
+ @Test
+ public void takeScreenshot_gameManagerSuccess_returnsBitmap() throws Exception {
+ doAnswer(invocation -> {
+ AndroidFuture result = invocation.getArgument(1);
+ result.complete(GameScreenshotResult.createSuccessResult(TEST_BITMAP));
+ return null;
+ }).when(mMockGameSessionController).takeScreenshot(anyInt(), any());
+
+ CountDownLatch countDownLatch = new CountDownLatch(1);
+
+ mGameSession.takeScreenshot(DIRECT_EXECUTOR,
+ new ScreenshotCallback() {
+ @Override
+ public void onFailure(int statusCode) {
+ fail();
+ }
+
+ @Override
+ public void onSuccess(Bitmap bitmap) {
+ assertEquals(TEST_BITMAP, bitmap);
+ countDownLatch.countDown();
+ }
+ });
+
+ assertTrue(countDownLatch.await(
+ WAIT_FOR_CALLBACK_TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ }
+
+ @Test
+ public void moveState_InitializedToInitialized_noLifecycleCalls() throws Exception {
+ mGameSession.moveToState(GameSession.LifecycleState.INITIALIZED);
+
+ assertThat(mGameSession.mLifecycleMethodCalls.isEmpty()).isTrue();
+ }
+
+ @Test
+ public void moveState_FullLifecycle_ExpectedLifecycleCalls() throws Exception {
+ mGameSession.moveToState(GameSession.LifecycleState.CREATED);
+ mGameSession.moveToState(GameSession.LifecycleState.TASK_FOCUSED);
+ mGameSession.moveToState(GameSession.LifecycleState.CREATED);
+ mGameSession.moveToState(GameSession.LifecycleState.DESTROYED);
+
+ assertThat(mGameSession.mLifecycleMethodCalls).containsExactly(
+ LifecycleTrackingGameSession.LifecycleMethodCall.ON_CREATE,
+ LifecycleTrackingGameSession.LifecycleMethodCall.ON_GAME_TASK_FOCUSED,
+ LifecycleTrackingGameSession.LifecycleMethodCall.ON_GAME_TASK_UNFOCUSED,
+ LifecycleTrackingGameSession.LifecycleMethodCall.ON_DESTROY).inOrder();
+ }
+
+ @Test
+ public void moveState_DestroyedWhenInitialized_ExpectedLifecycleCalls() throws Exception {
+ mGameSession.moveToState(GameSession.LifecycleState.DESTROYED);
+
+ // ON_CREATE is always called before ON_DESTROY.
+ assertThat(mGameSession.mLifecycleMethodCalls).containsExactly(
+ LifecycleTrackingGameSession.LifecycleMethodCall.ON_CREATE,
+ LifecycleTrackingGameSession.LifecycleMethodCall.ON_DESTROY).inOrder();
+ }
+
+ @Test
+ public void moveState_DestroyedWhenFocused_ExpectedLifecycleCalls() throws Exception {
+ mGameSession.moveToState(GameSession.LifecycleState.CREATED);
+ mGameSession.moveToState(GameSession.LifecycleState.TASK_FOCUSED);
+ mGameSession.moveToState(GameSession.LifecycleState.DESTROYED);
+
+ // The ON_GAME_TASK_UNFOCUSED lifecycle event is implied because the session is destroyed
+ // while in focus.
+ assertThat(mGameSession.mLifecycleMethodCalls).containsExactly(
+ LifecycleTrackingGameSession.LifecycleMethodCall.ON_CREATE,
+ LifecycleTrackingGameSession.LifecycleMethodCall.ON_GAME_TASK_FOCUSED,
+ LifecycleTrackingGameSession.LifecycleMethodCall.ON_GAME_TASK_UNFOCUSED,
+ LifecycleTrackingGameSession.LifecycleMethodCall.ON_DESTROY).inOrder();
+ }
+
+ @Test
+ public void moveState_FocusCycled_ExpectedLifecycleCalls() throws Exception {
+ mGameSession.moveToState(GameSession.LifecycleState.CREATED);
+ mGameSession.moveToState(GameSession.LifecycleState.TASK_FOCUSED);
+ mGameSession.moveToState(GameSession.LifecycleState.TASK_UNFOCUSED);
+ mGameSession.moveToState(GameSession.LifecycleState.TASK_FOCUSED);
+ mGameSession.moveToState(GameSession.LifecycleState.TASK_UNFOCUSED);
+
+ // Both cycles from focus and unfocus are captured.
+ assertThat(mGameSession.mLifecycleMethodCalls).containsExactly(
+ LifecycleTrackingGameSession.LifecycleMethodCall.ON_CREATE,
+ LifecycleTrackingGameSession.LifecycleMethodCall.ON_GAME_TASK_FOCUSED,
+ LifecycleTrackingGameSession.LifecycleMethodCall.ON_GAME_TASK_UNFOCUSED,
+ LifecycleTrackingGameSession.LifecycleMethodCall.ON_GAME_TASK_FOCUSED,
+ LifecycleTrackingGameSession.LifecycleMethodCall.ON_GAME_TASK_UNFOCUSED).inOrder();
+ }
+
+ @Test
+ public void moveState_MultipleFocusAndUnfocusCalls_ExpectedLifecycleCalls() throws Exception {
+ mGameSession.moveToState(GameSession.LifecycleState.CREATED);
+ mGameSession.moveToState(GameSession.LifecycleState.TASK_FOCUSED);
+ mGameSession.moveToState(GameSession.LifecycleState.TASK_FOCUSED);
+ mGameSession.moveToState(GameSession.LifecycleState.TASK_UNFOCUSED);
+ mGameSession.moveToState(GameSession.LifecycleState.TASK_UNFOCUSED);
+
+ // The second TASK_FOCUSED call and the second TASK_UNFOCUSED call are ignored.
+ assertThat(mGameSession.mLifecycleMethodCalls).containsExactly(
+ LifecycleTrackingGameSession.LifecycleMethodCall.ON_CREATE,
+ LifecycleTrackingGameSession.LifecycleMethodCall.ON_GAME_TASK_FOCUSED,
+ LifecycleTrackingGameSession.LifecycleMethodCall.ON_GAME_TASK_UNFOCUSED).inOrder();
+ }
+
+ @Test
+ public void moveState_CreatedAfterFocused_ExpectedLifecycleCalls() throws Exception {
+ mGameSession.moveToState(GameSession.LifecycleState.CREATED);
+ mGameSession.moveToState(GameSession.LifecycleState.TASK_FOCUSED);
+ mGameSession.moveToState(GameSession.LifecycleState.CREATED);
+
+ // The second CREATED call is ignored.
+ assertThat(mGameSession.mLifecycleMethodCalls).containsExactly(
+ LifecycleTrackingGameSession.LifecycleMethodCall.ON_CREATE,
+ LifecycleTrackingGameSession.LifecycleMethodCall.ON_GAME_TASK_FOCUSED).inOrder();
+ }
+
+ @Test
+ public void moveState_UnfocusedWithoutFocused_ExpectedLifecycleCalls() throws Exception {
+ mGameSession.moveToState(GameSession.LifecycleState.CREATED);
+ mGameSession.moveToState(GameSession.LifecycleState.TASK_UNFOCUSED);
+
+ // The TASK_UNFOCUSED call without an earlier TASK_FOCUSED call is ignored.
+ assertThat(mGameSession.mLifecycleMethodCalls).containsExactly(
+ LifecycleTrackingGameSession.LifecycleMethodCall.ON_CREATE).inOrder();
+ }
+
+ @Test
+ public void moveState_NeverFocused_ExpectedLifecycleCalls() throws Exception {
+ mGameSession.moveToState(GameSession.LifecycleState.CREATED);
+ mGameSession.moveToState(GameSession.LifecycleState.DESTROYED);
+
+ assertThat(mGameSession.mLifecycleMethodCalls).containsExactly(
+ LifecycleTrackingGameSession.LifecycleMethodCall.ON_CREATE,
+ LifecycleTrackingGameSession.LifecycleMethodCall.ON_DESTROY).inOrder();
+ }
+
+ @Test
+ public void moveState_MultipleFocusCalls_ExpectedLifecycleCalls() throws Exception {
+ mGameSession.moveToState(GameSession.LifecycleState.CREATED);
+ mGameSession.moveToState(GameSession.LifecycleState.TASK_FOCUSED);
+ mGameSession.moveToState(GameSession.LifecycleState.TASK_FOCUSED);
+ mGameSession.moveToState(GameSession.LifecycleState.TASK_FOCUSED);
+
+ // The extra TASK_FOCUSED moves are ignored.
+ assertThat(mGameSession.mLifecycleMethodCalls).containsExactly(
+ LifecycleTrackingGameSession.LifecycleMethodCall.ON_CREATE,
+ LifecycleTrackingGameSession.LifecycleMethodCall.ON_GAME_TASK_FOCUSED).inOrder();
+ }
+
+ @Test
+ public void moveState_MultipleCreateCalls_ExpectedLifecycleCalls() throws Exception {
+ mGameSession.moveToState(GameSession.LifecycleState.CREATED);
+ mGameSession.moveToState(GameSession.LifecycleState.CREATED);
+ mGameSession.moveToState(GameSession.LifecycleState.CREATED);
+
+ // The extra CREATE moves are ignored.
+ assertThat(mGameSession.mLifecycleMethodCalls).containsExactly(
+ LifecycleTrackingGameSession.LifecycleMethodCall.ON_CREATE).inOrder();
+ }
+
+ @Test
+ public void moveState_FocusBeforeCreate_ExpectedLifecycleCalls() throws Exception {
+ mGameSession.moveToState(GameSession.LifecycleState.TASK_FOCUSED);
+
+ // The TASK_FOCUSED move before CREATE is ignored.
+ assertThat(mGameSession.mLifecycleMethodCalls.isEmpty()).isTrue();
+ }
+
+ @Test
+ public void moveState_UnfocusBeforeCreate_ExpectedLifecycleCalls() throws Exception {
+ mGameSession.moveToState(GameSession.LifecycleState.TASK_UNFOCUSED);
+
+ // The TASK_UNFOCUSED move before CREATE is ignored.
+ assertThat(mGameSession.mLifecycleMethodCalls.isEmpty()).isTrue();
+ }
+
+ @Test
+ public void moveState_FocusWhenDestroyed_ExpectedLifecycleCalls() throws Exception {
+ mGameSession.moveToState(GameSession.LifecycleState.CREATED);
+ mGameSession.moveToState(GameSession.LifecycleState.DESTROYED);
+ mGameSession.moveToState(GameSession.LifecycleState.TASK_FOCUSED);
+
+ // The TASK_FOCUSED move after DESTROYED is ignored.
+ assertThat(mGameSession.mLifecycleMethodCalls).containsExactly(
+ LifecycleTrackingGameSession.LifecycleMethodCall.ON_CREATE,
+ LifecycleTrackingGameSession.LifecycleMethodCall.ON_DESTROY).inOrder();
+ }
+
+ private static class LifecycleTrackingGameSession extends GameSession {
+ private enum LifecycleMethodCall {
+ ON_CREATE,
+ ON_DESTROY,
+ ON_GAME_TASK_FOCUSED,
+ ON_GAME_TASK_UNFOCUSED
+ }
+
+ final List<LifecycleMethodCall> mLifecycleMethodCalls = new ArrayList<>();
+
+ @Override
+ public void onCreate() {
+ mLifecycleMethodCalls.add(LifecycleMethodCall.ON_CREATE);
+ }
+
+ @Override
+ public void onDestroy() {
+ mLifecycleMethodCalls.add(LifecycleMethodCall.ON_DESTROY);
+ }
+
+ @Override
+ public void onGameTaskFocusChanged(boolean focused) {
+ if (focused) {
+ mLifecycleMethodCalls.add(LifecycleMethodCall.ON_GAME_TASK_FOCUSED);
+ } else {
+ mLifecycleMethodCalls.add(LifecycleMethodCall.ON_GAME_TASK_UNFOCUSED);
+ }
+ }
+ }
+}
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 1c21645c1626..0198253e2586 100644
--- a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
@@ -20,6 +20,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSess
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -28,6 +29,7 @@ import static org.mockito.Mockito.when;
import android.Manifest;
import android.app.GameManager;
+import android.app.GameModeInfo;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.pm.ApplicationInfo;
@@ -190,7 +192,7 @@ public class GameManagerServiceTests {
}
private void mockDeviceConfigPerformance() {
- String configString = "mode=2,downscaleFactor=0.5,useAngle=false";
+ String configString = "mode=2,downscaleFactor=0.5,useAngle=false,fps=90";
when(DeviceConfig.getProperty(anyString(), anyString()))
.thenReturn(configString);
}
@@ -203,13 +205,13 @@ public class GameManagerServiceTests {
}
private void mockDeviceConfigBattery() {
- String configString = "mode=3,downscaleFactor=0.7";
+ String configString = "mode=3,downscaleFactor=0.7,fps=30";
when(DeviceConfig.getProperty(anyString(), anyString()))
.thenReturn(configString);
}
private void mockDeviceConfigAll() {
- String configString = "mode=3,downscaleFactor=0.7:mode=2,downscaleFactor=0.5";
+ String configString = "mode=3,downscaleFactor=0.7,fps=30:mode=2,downscaleFactor=0.5,fps=90";
when(DeviceConfig.getProperty(anyString(), anyString()))
.thenReturn(configString);
}
@@ -454,12 +456,12 @@ public class GameManagerServiceTests {
gameManagerService.getGameMode(mPackageName, USER_ID_2));
}
- private void checkReportedModes(int ...requiredModes) {
- GameManagerService gameManagerService =
- new GameManagerService(mMockContext, mTestLooper.getLooper());
-
- startUser(gameManagerService, USER_ID_1);
- gameManagerService.updateConfigsForUser(USER_ID_1, mPackageName);
+ private void checkReportedModes(GameManagerService gameManagerService, int ...requiredModes) {
+ if (gameManagerService == null) {
+ gameManagerService = new GameManagerService(mMockContext, mTestLooper.getLooper());
+ startUser(gameManagerService, USER_ID_1);
+ gameManagerService.updateConfigsForUser(USER_ID_1, mPackageName);
+ }
ArraySet<Integer> reportedModes = new ArraySet<>();
int[] modes = gameManagerService.getAvailableGameModes(mPackageName);
for (int mode : modes) {
@@ -472,12 +474,13 @@ public class GameManagerServiceTests {
}
}
- private void checkDownscaling(int gameMode, String scaling) {
- GameManagerService gameManagerService =
- new GameManagerService(mMockContext, mTestLooper.getLooper());
-
- startUser(gameManagerService, USER_ID_1);
- gameManagerService.updateConfigsForUser(USER_ID_1, mPackageName);
+ private void checkDownscaling(GameManagerService gameManagerService,
+ int gameMode, String scaling) {
+ if (gameManagerService == null) {
+ gameManagerService = new GameManagerService(mMockContext, mTestLooper.getLooper());
+ startUser(gameManagerService, USER_ID_1);
+ gameManagerService.updateConfigsForUser(USER_ID_1, mPackageName);
+ }
GameManagerService.GamePackageConfiguration config =
gameManagerService.getConfig(mPackageName);
assertEquals(config.getGameModeConfiguration(gameMode).getScaling(), scaling);
@@ -496,6 +499,17 @@ public class GameManagerServiceTests {
assertEquals(gameManagerService.getAngleEnabled(mPackageName, USER_ID_1), angleEnabled);
}
+ private void checkFps(GameManagerService gameManagerService, int gameMode, int fps) {
+ if (gameManagerService == null) {
+ gameManagerService = new GameManagerService(mMockContext, mTestLooper.getLooper());
+ startUser(gameManagerService, USER_ID_1);
+ gameManagerService.updateConfigsForUser(USER_ID_1, mPackageName);
+ }
+ GameManagerService.GamePackageConfiguration config =
+ gameManagerService.getConfig(mPackageName);
+ assertEquals(config.getGameModeConfiguration(gameMode).getFps(), fps);
+ }
+
/**
* Phenotype device config exists, but is only propagating the default value.
*/
@@ -503,7 +517,7 @@ public class GameManagerServiceTests {
public void testDeviceConfigDefault() {
mockDeviceConfigDefault();
mockModifyGameModeGranted();
- checkReportedModes(GameManager.GAME_MODE_UNSUPPORTED);
+ checkReportedModes(null);
}
/**
@@ -513,7 +527,7 @@ public class GameManagerServiceTests {
public void testDeviceConfigNone() {
mockDeviceConfigNone();
mockModifyGameModeGranted();
- checkReportedModes(GameManager.GAME_MODE_UNSUPPORTED);
+ checkReportedModes(null);
}
/**
@@ -523,7 +537,7 @@ public class GameManagerServiceTests {
public void testDeviceConfigPerformance() {
mockDeviceConfigPerformance();
mockModifyGameModeGranted();
- checkReportedModes(GameManager.GAME_MODE_PERFORMANCE, GameManager.GAME_MODE_STANDARD);
+ checkReportedModes(null, GameManager.GAME_MODE_PERFORMANCE, GameManager.GAME_MODE_STANDARD);
}
/**
@@ -533,7 +547,7 @@ public class GameManagerServiceTests {
public void testDeviceConfigBattery() {
mockDeviceConfigBattery();
mockModifyGameModeGranted();
- checkReportedModes(GameManager.GAME_MODE_BATTERY, GameManager.GAME_MODE_STANDARD);
+ checkReportedModes(null, GameManager.GAME_MODE_BATTERY, GameManager.GAME_MODE_STANDARD);
}
/**
@@ -543,7 +557,7 @@ public class GameManagerServiceTests {
public void testDeviceConfigAll() {
mockDeviceConfigAll();
mockModifyGameModeGranted();
- checkReportedModes(GameManager.GAME_MODE_PERFORMANCE, GameManager.GAME_MODE_BATTERY,
+ checkReportedModes(null, GameManager.GAME_MODE_PERFORMANCE, GameManager.GAME_MODE_BATTERY,
GameManager.GAME_MODE_STANDARD);
}
@@ -554,7 +568,7 @@ public class GameManagerServiceTests {
public void testDeviceConfigInvalid() {
mockDeviceConfigInvalid();
mockModifyGameModeGranted();
- checkReportedModes(GameManager.GAME_MODE_UNSUPPORTED);
+ checkReportedModes(null);
}
/**
@@ -564,7 +578,171 @@ public class GameManagerServiceTests {
public void testDeviceConfigMalformed() {
mockDeviceConfigMalformed();
mockModifyGameModeGranted();
- checkReportedModes(GameManager.GAME_MODE_UNSUPPORTED);
+ checkReportedModes(null);
+ }
+
+ /**
+ * Override device config for performance mode exists and is valid.
+ */
+ @Test
+ public void testSetDeviceConfigOverridePerformance() {
+ mockDeviceConfigPerformance();
+ mockModifyGameModeGranted();
+
+ GameManagerService gameManagerService = new GameManagerService(
+ mMockContext, mTestLooper.getLooper());
+ startUser(gameManagerService, USER_ID_1);
+ gameManagerService.setGameModeConfigOverride(mPackageName, USER_ID_1,
+ GameManager.GAME_MODE_PERFORMANCE, "120", "0.3");
+
+ checkReportedModes(gameManagerService, GameManager.GAME_MODE_PERFORMANCE,
+ GameManager.GAME_MODE_STANDARD);
+ checkDownscaling(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, "0.3");
+ checkFps(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 120);
+ }
+
+ /**
+ * Override device config for battery mode exists and is valid.
+ */
+ @Test
+ public void testSetDeviceConfigOverrideBattery() {
+ mockDeviceConfigBattery();
+ mockModifyGameModeGranted();
+
+ GameManagerService gameManagerService = new GameManagerService(
+ mMockContext, mTestLooper.getLooper());
+ startUser(gameManagerService, USER_ID_1);
+ gameManagerService.setGameModeConfigOverride(mPackageName, USER_ID_1,
+ GameManager.GAME_MODE_BATTERY, "60", "0.5");
+
+ checkReportedModes(gameManagerService, GameManager.GAME_MODE_BATTERY,
+ GameManager.GAME_MODE_STANDARD);
+ checkDownscaling(gameManagerService, GameManager.GAME_MODE_BATTERY, "0.5");
+ checkFps(gameManagerService, GameManager.GAME_MODE_BATTERY, 60);
+ }
+
+ /**
+ * Override device configs for both battery and performance modes exists and are valid.
+ */
+ @Test
+ public void testSetDeviceOverrideConfigAll() {
+ mockDeviceConfigAll();
+ mockModifyGameModeGranted();
+
+ GameManagerService gameManagerService = new GameManagerService(
+ mMockContext, mTestLooper.getLooper());
+ startUser(gameManagerService, USER_ID_1);
+ gameManagerService.setGameModeConfigOverride(mPackageName, USER_ID_1,
+ GameManager.GAME_MODE_PERFORMANCE, "120", "0.3");
+ gameManagerService.setGameModeConfigOverride(mPackageName, USER_ID_1,
+ GameManager.GAME_MODE_BATTERY, "60", "0.5");
+
+ checkReportedModes(gameManagerService, GameManager.GAME_MODE_PERFORMANCE,
+ GameManager.GAME_MODE_BATTERY, GameManager.GAME_MODE_STANDARD);
+ checkDownscaling(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, "0.3");
+ checkFps(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 120);
+ checkDownscaling(gameManagerService, GameManager.GAME_MODE_BATTERY, "0.5");
+ checkFps(gameManagerService, GameManager.GAME_MODE_BATTERY, 60);
+ }
+
+ /**
+ * Override device config for performance mode exists and is valid.
+ */
+ @Test
+ public void testResetDeviceConfigOverridePerformance() {
+ mockDeviceConfigPerformance();
+ mockModifyGameModeGranted();
+
+ GameManagerService gameManagerService = new GameManagerService(
+ mMockContext, mTestLooper.getLooper());
+ startUser(gameManagerService, USER_ID_1);
+ gameManagerService.setGameModeConfigOverride(mPackageName, USER_ID_1,
+ GameManager.GAME_MODE_PERFORMANCE, "120", "0.3");
+
+ gameManagerService.resetGameModeConfigOverride(mPackageName, USER_ID_1,
+ GameManager.GAME_MODE_PERFORMANCE);
+
+ checkReportedModes(gameManagerService, GameManager.GAME_MODE_PERFORMANCE,
+ GameManager.GAME_MODE_STANDARD);
+ checkDownscaling(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, "0.5");
+ checkFps(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 90);
+ }
+
+ /**
+ * Override device config for battery mode exists and is valid.
+ */
+ @Test
+ public void testResetDeviceConfigOverrideBattery() {
+ mockDeviceConfigBattery();
+ mockModifyGameModeGranted();
+
+ GameManagerService gameManagerService = new GameManagerService(
+ mMockContext, mTestLooper.getLooper());
+ startUser(gameManagerService, USER_ID_1);
+ gameManagerService.setGameModeConfigOverride(mPackageName, USER_ID_1,
+ GameManager.GAME_MODE_BATTERY, "60", "0.5");
+
+ gameManagerService.resetGameModeConfigOverride(mPackageName, USER_ID_1,
+ GameManager.GAME_MODE_BATTERY);
+
+ checkReportedModes(gameManagerService, GameManager.GAME_MODE_BATTERY,
+ GameManager.GAME_MODE_STANDARD);
+ checkDownscaling(gameManagerService, GameManager.GAME_MODE_BATTERY, "0.7");
+ checkFps(gameManagerService, GameManager.GAME_MODE_BATTERY, 30);
+ }
+
+ /**
+ * Override device configs for both battery and performance modes exists and are valid.
+ */
+ @Test
+ public void testResetDeviceOverrideConfigAll() {
+ mockDeviceConfigAll();
+ mockModifyGameModeGranted();
+
+ GameManagerService gameManagerService = new GameManagerService(
+ mMockContext, mTestLooper.getLooper());
+ startUser(gameManagerService, USER_ID_1);
+ gameManagerService.setGameModeConfigOverride(mPackageName, USER_ID_1,
+ GameManager.GAME_MODE_PERFORMANCE, "120", "0.3");
+ gameManagerService.setGameModeConfigOverride(mPackageName, USER_ID_1,
+ GameManager.GAME_MODE_BATTERY, "60", "0.5");
+
+ gameManagerService.resetGameModeConfigOverride(mPackageName, USER_ID_1, -1);
+
+ checkReportedModes(gameManagerService, GameManager.GAME_MODE_PERFORMANCE,
+ GameManager.GAME_MODE_BATTERY, GameManager.GAME_MODE_STANDARD);
+ checkDownscaling(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, "0.5");
+ checkFps(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 90);
+ checkDownscaling(gameManagerService, GameManager.GAME_MODE_BATTERY, "0.7");
+ checkFps(gameManagerService, GameManager.GAME_MODE_BATTERY, 30);
+ }
+
+ /**
+ * Override device configs for both battery and performance modes exists and are valid.
+ * Only one mode is reset, and the other mode still has overridden config
+ */
+ @Test
+ public void testResetDeviceOverrideConfigPartial() {
+ mockDeviceConfigAll();
+ mockModifyGameModeGranted();
+
+ GameManagerService gameManagerService = new GameManagerService(
+ mMockContext, mTestLooper.getLooper());
+ startUser(gameManagerService, USER_ID_1);
+ gameManagerService.setGameModeConfigOverride(mPackageName, USER_ID_1,
+ GameManager.GAME_MODE_PERFORMANCE, "120", "0.3");
+ gameManagerService.setGameModeConfigOverride(mPackageName, USER_ID_1,
+ GameManager.GAME_MODE_BATTERY, "60", "0.5");
+
+ gameManagerService.resetGameModeConfigOverride(mPackageName, USER_ID_1,
+ GameManager.GAME_MODE_BATTERY);
+
+ checkReportedModes(gameManagerService, GameManager.GAME_MODE_PERFORMANCE,
+ GameManager.GAME_MODE_BATTERY, GameManager.GAME_MODE_STANDARD);
+ checkDownscaling(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, "0.3");
+ checkFps(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, 120);
+ checkDownscaling(gameManagerService, GameManager.GAME_MODE_BATTERY, "0.7");
+ checkFps(gameManagerService, GameManager.GAME_MODE_BATTERY, 30);
}
/**
@@ -575,10 +753,12 @@ public class GameManagerServiceTests {
mockGameModeOptInAll();
mockDeviceConfigNone();
mockModifyGameModeGranted();
- checkReportedModes(GameManager.GAME_MODE_PERFORMANCE, GameManager.GAME_MODE_BATTERY,
+ checkReportedModes(null, GameManager.GAME_MODE_PERFORMANCE, GameManager.GAME_MODE_BATTERY,
GameManager.GAME_MODE_STANDARD);
}
+
+
/**
* BATTERY game mode is available through the app manifest opt-in.
*/
@@ -587,7 +767,7 @@ public class GameManagerServiceTests {
mockGameModeOptInBattery();
mockDeviceConfigNone();
mockModifyGameModeGranted();
- checkReportedModes(GameManager.GAME_MODE_BATTERY, GameManager.GAME_MODE_STANDARD);
+ checkReportedModes(null, GameManager.GAME_MODE_BATTERY, GameManager.GAME_MODE_STANDARD);
}
/**
@@ -598,7 +778,7 @@ public class GameManagerServiceTests {
mockGameModeOptInPerformance();
mockDeviceConfigNone();
mockModifyGameModeGranted();
- checkReportedModes(GameManager.GAME_MODE_PERFORMANCE, GameManager.GAME_MODE_STANDARD);
+ checkReportedModes(null, GameManager.GAME_MODE_PERFORMANCE, GameManager.GAME_MODE_STANDARD);
}
/**
@@ -610,7 +790,7 @@ public class GameManagerServiceTests {
mockGameModeOptInBattery();
mockDeviceConfigPerformance();
mockModifyGameModeGranted();
- checkReportedModes(GameManager.GAME_MODE_PERFORMANCE, GameManager.GAME_MODE_BATTERY,
+ checkReportedModes(null, GameManager.GAME_MODE_PERFORMANCE, GameManager.GAME_MODE_BATTERY,
GameManager.GAME_MODE_STANDARD);
}
@@ -623,7 +803,7 @@ public class GameManagerServiceTests {
mockGameModeOptInPerformance();
mockDeviceConfigBattery();
mockModifyGameModeGranted();
- checkReportedModes(GameManager.GAME_MODE_PERFORMANCE, GameManager.GAME_MODE_BATTERY,
+ checkReportedModes(null, GameManager.GAME_MODE_PERFORMANCE, GameManager.GAME_MODE_BATTERY,
GameManager.GAME_MODE_STANDARD);
}
@@ -634,7 +814,7 @@ public class GameManagerServiceTests {
public void testInterventionAllowScalingDefault() throws Exception {
mockDeviceConfigPerformance();
mockModifyGameModeGranted();
- checkDownscaling(GameManager.GAME_MODE_PERFORMANCE, "0.5");
+ checkDownscaling(null, GameManager.GAME_MODE_PERFORMANCE, "0.5");
}
/**
@@ -645,7 +825,7 @@ public class GameManagerServiceTests {
mockDeviceConfigPerformance();
mockInterventionAllowDownscaleFalse();
mockModifyGameModeGranted();
- checkDownscaling(GameManager.GAME_MODE_PERFORMANCE, "1.0");
+ checkDownscaling(null, GameManager.GAME_MODE_PERFORMANCE, "1.0");
}
/**
@@ -657,7 +837,7 @@ public class GameManagerServiceTests {
mockDeviceConfigPerformance();
mockInterventionAllowDownscaleTrue();
mockModifyGameModeGranted();
- checkDownscaling(GameManager.GAME_MODE_PERFORMANCE, "0.5");
+ checkDownscaling(null, GameManager.GAME_MODE_PERFORMANCE, "0.5");
}
/**
@@ -708,6 +888,14 @@ public class GameManagerServiceTests {
checkAngleEnabled(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, true);
}
+ @Test
+ public void testInterventionFps() throws Exception {
+ mockDeviceConfigAll();
+ mockModifyGameModeGranted();
+ checkFps(null, GameManager.GAME_MODE_PERFORMANCE, 90);
+ checkFps(null, GameManager.GAME_MODE_BATTERY, 30);
+ }
+
/**
* 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.
@@ -776,4 +964,88 @@ public class GameManagerServiceTests {
assertEquals(GameManager.GAME_MODE_STANDARD,
gameManagerService.getGameMode(mPackageName, USER_ID_1));
}
+
+ static {
+ System.loadLibrary("mockingservicestestjni");
+ }
+ @Test
+ public void testGetGameModeInfoPermissionDenied() {
+ mockDeviceConfigAll();
+ GameManagerService gameManagerService =
+ new GameManagerService(mMockContext, mTestLooper.getLooper());
+ startUser(gameManagerService, USER_ID_1);
+
+ // Deny permission.MANAGE_GAME_MODE and verify the game mode is not updated.
+ mockModifyGameModeDenied();
+ assertThrows(SecurityException.class,
+ () -> gameManagerService.getGameModeInfo(mPackageName, USER_ID_1));
+ }
+
+ @Test
+ public void testGetGameModeInfoWithAllGameModesDefault() {
+ mockDeviceConfigAll();
+ mockModifyGameModeGranted();
+ GameManagerService gameManagerService =
+ new GameManagerService(mMockContext, mTestLooper.getLooper());
+ startUser(gameManagerService, USER_ID_1);
+ GameModeInfo gameModeInfo = gameManagerService.getGameModeInfo(mPackageName, USER_ID_1);
+
+ assertEquals(GameManager.GAME_MODE_STANDARD, gameModeInfo.getActiveGameMode());
+ assertEquals(3, gameModeInfo.getAvailableGameModes().length);
+ }
+
+ @Test
+ public void testGetGameModeInfoWithAllGameModes() {
+ mockDeviceConfigAll();
+ mockModifyGameModeGranted();
+ GameManagerService gameManagerService =
+ new GameManagerService(mMockContext, mTestLooper.getLooper());
+ startUser(gameManagerService, USER_ID_1);
+ gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_PERFORMANCE, USER_ID_1);
+ GameModeInfo gameModeInfo = gameManagerService.getGameModeInfo(mPackageName, USER_ID_1);
+
+ assertEquals(GameManager.GAME_MODE_PERFORMANCE, gameModeInfo.getActiveGameMode());
+ assertEquals(3, gameModeInfo.getAvailableGameModes().length);
+ }
+
+ @Test
+ public void testGetGameModeInfoWithBatteryMode() {
+ mockDeviceConfigBattery();
+ mockModifyGameModeGranted();
+ GameManagerService gameManagerService =
+ new GameManagerService(mMockContext, mTestLooper.getLooper());
+ startUser(gameManagerService, USER_ID_1);
+ gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_BATTERY, USER_ID_1);
+ GameModeInfo gameModeInfo = gameManagerService.getGameModeInfo(mPackageName, USER_ID_1);
+
+ assertEquals(GameManager.GAME_MODE_BATTERY, gameModeInfo.getActiveGameMode());
+ assertEquals(2, gameModeInfo.getAvailableGameModes().length);
+ }
+
+ @Test
+ public void testGetGameModeInfoWithPerformanceMode() {
+ mockDeviceConfigPerformance();
+ mockModifyGameModeGranted();
+ GameManagerService gameManagerService =
+ new GameManagerService(mMockContext, mTestLooper.getLooper());
+ startUser(gameManagerService, USER_ID_1);
+ gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_PERFORMANCE, USER_ID_1);
+ GameModeInfo gameModeInfo = gameManagerService.getGameModeInfo(mPackageName, USER_ID_1);
+
+ assertEquals(GameManager.GAME_MODE_PERFORMANCE, gameModeInfo.getActiveGameMode());
+ assertEquals(2, gameModeInfo.getAvailableGameModes().length);
+ }
+
+ @Test
+ public void testGetGameModeInfoWithUnsupportedGameMode() {
+ mockDeviceConfigNone();
+ mockModifyGameModeGranted();
+ GameManagerService gameManagerService =
+ new GameManagerService(mMockContext, mTestLooper.getLooper());
+ startUser(gameManagerService, USER_ID_1);
+ GameModeInfo gameModeInfo = gameManagerService.getGameModeInfo(mPackageName, USER_ID_1);
+
+ assertEquals(GameManager.GAME_MODE_UNSUPPORTED, gameModeInfo.getActiveGameMode());
+ assertEquals(0, gameModeInfo.getAvailableGameModes().length);
+ }
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java b/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java
index 0d513bb2d68c..bdfa3bfedb55 100644
--- a/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java
@@ -18,29 +18,43 @@ package com.android.server.app;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.server.app.GameServiceProviderInstanceImplTest.FakeGameService.GameServiceState;
+import static com.google.common.collect.Iterables.getOnlyElement;
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertEquals;
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.inOrder;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
import android.annotation.Nullable;
+import android.app.ActivityManager.RunningTaskInfo;
+import android.app.ActivityTaskManager;
import android.app.IActivityTaskManager;
import android.app.ITaskStackListener;
import android.content.ComponentName;
import android.content.pm.PackageManager;
-import android.os.IBinder;
+import android.graphics.Bitmap;
+import android.graphics.Rect;
import android.os.RemoteException;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
import android.service.games.CreateGameSessionRequest;
+import android.service.games.CreateGameSessionResult;
+import android.service.games.GameScreenshotResult;
+import android.service.games.GameSessionViewHostConfiguration;
import android.service.games.GameStartedEvent;
import android.service.games.IGameService;
import android.service.games.IGameServiceController;
import android.service.games.IGameSession;
+import android.service.games.IGameSessionController;
import android.service.games.IGameSessionService;
+import android.view.SurfaceControlViewHost.SurfacePackage;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -48,20 +62,21 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.internal.infra.AndroidFuture;
import com.android.internal.util.ConcurrentUtils;
import com.android.internal.util.FunctionalUtils.ThrowingConsumer;
+import com.android.internal.util.Preconditions;
+import com.android.server.wm.WindowManagerInternal;
+import com.android.server.wm.WindowManagerService;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.InOrder;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.MockitoSession;
import org.mockito.quality.Strictness;
import java.util.ArrayList;
-import java.util.concurrent.atomic.AtomicReference;
-import java.util.function.Supplier;
+import java.util.HashMap;
/**
@@ -72,6 +87,9 @@ import java.util.function.Supplier;
@Presubmit
public final class GameServiceProviderInstanceImplTest {
+ private static final GameSessionViewHostConfiguration
+ DEFAULT_GAME_SESSION_VIEW_HOST_CONFIGURATION =
+ new GameSessionViewHostConfiguration(1, 500, 800);
private static final int USER_ID = 10;
private static final String APP_A_PACKAGE = "com.package.app.a";
private static final ComponentName APP_A_MAIN_ACTIVITY =
@@ -81,19 +99,23 @@ public final class GameServiceProviderInstanceImplTest {
private static final ComponentName GAME_A_MAIN_ACTIVITY =
new ComponentName(GAME_A_PACKAGE, "com.package.game.a.MainActivity");
+ private static final Bitmap TEST_BITMAP = Bitmap.createBitmap(32, 32, Bitmap.Config.ARGB_8888);
+
private MockitoSession mMockingSession;
private GameServiceProviderInstance mGameServiceProviderInstance;
@Mock
private IActivityTaskManager mMockActivityTaskManager;
@Mock
- private IGameService mMockGameService;
+ private WindowManagerService mMockWindowManagerService;
@Mock
- private IGameSessionService mMockGameSessionService;
+ private WindowManagerInternal mMockWindowManagerInternal;
private FakeGameClassifier mFakeGameClassifier;
+ private FakeGameService mFakeGameService;
private FakeServiceConnector<IGameService> mFakeGameServiceConnector;
+ private FakeGameSessionService mFakeGameSessionService;
private FakeServiceConnector<IGameSessionService> mFakeGameSessionServiceConnector;
private ArrayList<ITaskStackListener> mTaskStackListeners;
- private InOrder mInOrder;
+ private ArrayList<RunningTaskInfo> mRunningTaskInfos;
@Before
public void setUp() throws PackageManager.NameNotFoundException, RemoteException {
@@ -102,13 +124,13 @@ public final class GameServiceProviderInstanceImplTest {
.strictness(Strictness.LENIENT)
.startMocking();
- mInOrder = inOrder(mMockGameService, mMockGameSessionService);
-
mFakeGameClassifier = new FakeGameClassifier();
mFakeGameClassifier.recordGamePackage(GAME_A_PACKAGE);
- mFakeGameServiceConnector = new FakeServiceConnector<>(mMockGameService);
- mFakeGameSessionServiceConnector = new FakeServiceConnector<>(mMockGameSessionService);
+ mFakeGameService = new FakeGameService();
+ mFakeGameServiceConnector = new FakeServiceConnector<>(mFakeGameService);
+ mFakeGameSessionService = new FakeGameSessionService();
+ mFakeGameSessionServiceConnector = new FakeServiceConnector<>(mFakeGameSessionService);
mTaskStackListeners = new ArrayList<>();
doAnswer(invocation -> {
@@ -116,6 +138,10 @@ public final class GameServiceProviderInstanceImplTest {
return null;
}).when(mMockActivityTaskManager).registerTaskStackListener(any());
+ mRunningTaskInfos = new ArrayList<>();
+ when(mMockActivityTaskManager.getTasks(anyInt(), anyBoolean(), anyBoolean())).thenReturn(
+ mRunningTaskInfos);
+
doAnswer(invocation -> {
mTaskStackListeners.remove(invocation.getArgument(0));
return null;
@@ -126,6 +152,8 @@ public final class GameServiceProviderInstanceImplTest {
ConcurrentUtils.DIRECT_EXECUTOR,
mFakeGameClassifier,
mMockActivityTaskManager,
+ mMockWindowManagerService,
+ mMockWindowManagerInternal,
mFakeGameServiceConnector,
mFakeGameSessionServiceConnector);
}
@@ -139,8 +167,7 @@ public final class GameServiceProviderInstanceImplTest {
public void start_startsGameSession() throws Exception {
mGameServiceProviderInstance.start();
- mInOrder.verify(mMockGameService).connected(any());
- mInOrder.verifyNoMoreInteractions();
+ assertThat(mFakeGameService.getState()).isEqualTo(GameServiceState.CONNECTED);
assertThat(mFakeGameServiceConnector.getIsConnected()).isTrue();
assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(1);
assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(0);
@@ -149,9 +176,9 @@ public final class GameServiceProviderInstanceImplTest {
@Test
public void start_multipleTimes_startsGameSessionOnce() throws Exception {
mGameServiceProviderInstance.start();
+ mGameServiceProviderInstance.start();
- mInOrder.verify(mMockGameService).connected(any());
- mInOrder.verifyNoMoreInteractions();
+ assertThat(mFakeGameService.getState()).isEqualTo(GameServiceState.CONNECTED);
assertThat(mFakeGameServiceConnector.getIsConnected()).isTrue();
assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(1);
assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(0);
@@ -161,9 +188,10 @@ public final class GameServiceProviderInstanceImplTest {
public void stop_neverStarted_doesNothing() throws Exception {
mGameServiceProviderInstance.stop();
+
+ assertThat(mFakeGameService.getState()).isEqualTo(GameServiceState.DISCONNECTED);
assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(0);
assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(0);
- mInOrder.verifyNoMoreInteractions();
}
@Test
@@ -171,9 +199,8 @@ public final class GameServiceProviderInstanceImplTest {
mGameServiceProviderInstance.start();
mGameServiceProviderInstance.stop();
- mInOrder.verify(mMockGameService).connected(any());
- mInOrder.verify(mMockGameService).disconnected();
- mInOrder.verifyNoMoreInteractions();
+ assertThat(mFakeGameService.getState()).isEqualTo(GameServiceState.DISCONNECTED);
+ assertThat(mFakeGameService.getConnectedCount()).isEqualTo(1);
assertThat(mFakeGameServiceConnector.getIsConnected()).isFalse();
assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(1);
assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(0);
@@ -187,11 +214,8 @@ public final class GameServiceProviderInstanceImplTest {
mGameServiceProviderInstance.start();
mGameServiceProviderInstance.stop();
- mInOrder.verify(mMockGameService).connected(any());
- mInOrder.verify(mMockGameService).disconnected();
- mInOrder.verify(mMockGameService).connected(any());
- mInOrder.verify(mMockGameService).disconnected();
- mInOrder.verifyNoMoreInteractions();
+ assertThat(mFakeGameService.getState()).isEqualTo(GameServiceState.DISCONNECTED);
+ assertThat(mFakeGameService.getConnectedCount()).isEqualTo(2);
assertThat(mFakeGameServiceConnector.getIsConnected()).isFalse();
assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(2);
assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(0);
@@ -203,9 +227,8 @@ public final class GameServiceProviderInstanceImplTest {
mGameServiceProviderInstance.stop();
mGameServiceProviderInstance.stop();
- mInOrder.verify(mMockGameService).connected(any());
- mInOrder.verify(mMockGameService).disconnected();
- mInOrder.verifyNoMoreInteractions();
+ assertThat(mFakeGameService.getState()).isEqualTo(GameServiceState.DISCONNECTED);
+ assertThat(mFakeGameService.getConnectedCount()).isEqualTo(1);
assertThat(mFakeGameServiceConnector.getIsConnected()).isFalse();
assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(1);
assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(0);
@@ -215,7 +238,6 @@ public final class GameServiceProviderInstanceImplTest {
public void gameTaskStarted_neverStarted_doesNothing() throws Exception {
dispatchTaskCreated(10, GAME_A_MAIN_ACTIVITY);
- mInOrder.verifyNoMoreInteractions();
assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(0);
assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(0);
}
@@ -224,35 +246,25 @@ public final class GameServiceProviderInstanceImplTest {
public void gameTaskRemoved_neverStarted_doesNothing() throws Exception {
dispatchTaskRemoved(10);
- mInOrder.verifyNoMoreInteractions();
assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(0);
assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(0);
}
@Test
- public void gameTaskStarted_afterStopped_doesNothing() throws Exception {
+ public void gameTaskStarted_afterStopped_doesNotSendGameStartedEvent() throws Exception {
mGameServiceProviderInstance.start();
mGameServiceProviderInstance.stop();
dispatchTaskCreated(10, GAME_A_MAIN_ACTIVITY);
- mInOrder.verify(mMockGameService).connected(any());
- mInOrder.verify(mMockGameService).disconnected();
- mInOrder.verifyNoMoreInteractions();
- assertThat(mFakeGameServiceConnector.getIsConnected()).isFalse();
- assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(1);
- assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(0);
+ assertThat(mFakeGameService.getGameStartedEvents()).isEmpty();
}
@Test
- public void appTaskStarted_doesNothing() throws Exception {
+ public void appTaskStarted_doesNotSendGameStartedEvent() throws Exception {
mGameServiceProviderInstance.start();
dispatchTaskCreated(10, APP_A_MAIN_ACTIVITY);
- mInOrder.verify(mMockGameService).connected(any());
- mInOrder.verifyNoMoreInteractions();
- assertThat(mFakeGameServiceConnector.getIsConnected()).isTrue();
- assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(1);
- assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(0);
+ assertThat(mFakeGameService.getGameStartedEvents()).isEmpty();
}
@Test
@@ -260,26 +272,17 @@ public final class GameServiceProviderInstanceImplTest {
mGameServiceProviderInstance.start();
dispatchTaskCreated(10, null);
- mInOrder.verify(mMockGameService).connected(any());
- mInOrder.verifyNoMoreInteractions();
- assertThat(mFakeGameServiceConnector.getIsConnected()).isTrue();
- assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(1);
- assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(0);
+ assertThat(mFakeGameService.getGameStartedEvents()).isEmpty();
}
@Test
- public void gameSessionRequested_withoutTaskDispatch_ignoredAndDoesNotCrash() throws Exception {
+ public void gameSessionRequested_withoutTaskDispatch_doesNotCrashAndDoesNotCreateGameSession()
+ throws Exception {
mGameServiceProviderInstance.start();
- ArgumentCaptor<IGameServiceController> controllerArgumentCaptor = ArgumentCaptor.forClass(
- IGameServiceController.class);
- verify(mMockGameService).connected(controllerArgumentCaptor.capture());
- controllerArgumentCaptor.getValue().createGameSession(10);
- mInOrder.verify(mMockGameService).connected(any());
- mInOrder.verifyNoMoreInteractions();
- assertThat(mFakeGameServiceConnector.getIsConnected()).isTrue();
- assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(1);
- assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(0);
+ mFakeGameService.requestCreateGameSession(10);
+
+ assertThat(mFakeGameSessionService.getCapturedCreateInvocations()).isEmpty();
}
@Test
@@ -287,433 +290,410 @@ public final class GameServiceProviderInstanceImplTest {
mGameServiceProviderInstance.start();
dispatchTaskCreated(10, GAME_A_MAIN_ACTIVITY);
- mInOrder.verify(mMockGameService).connected(any());
- mInOrder.verify(mMockGameService).gameStarted(
- eq(new GameStartedEvent(10, GAME_A_PACKAGE)));
- mInOrder.verifyNoMoreInteractions();
- assertThat(mFakeGameServiceConnector.getIsConnected()).isTrue();
- assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(1);
- assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(0);
+ GameStartedEvent expectedGameStartedEvent = new GameStartedEvent(10, GAME_A_PACKAGE);
+ assertThat(mFakeGameService.getGameStartedEvents())
+ .containsExactly(expectedGameStartedEvent).inOrder();
}
@Test
- public void gameTaskStartedAndSessionRequested_createsGameSession() throws Exception {
- CreateGameSessionRequest createGameSessionRequest =
- new CreateGameSessionRequest(10, GAME_A_PACKAGE);
- Supplier<AndroidFuture<IBinder>> gameSession10Future =
- captureCreateGameSessionFuture(createGameSessionRequest);
+ public void gameTaskStarted_requestToCreateGameSessionIncludesTaskConfiguration()
+ throws Exception {
+ mGameServiceProviderInstance.start();
+ startTask(10, GAME_A_MAIN_ACTIVITY);
+ mFakeGameService.requestCreateGameSession(10);
+
+ FakeGameSessionService.CapturedCreateInvocation capturedCreateInvocation =
+ getOnlyElement(mFakeGameSessionService.getCapturedCreateInvocations());
+ assertThat(capturedCreateInvocation.mGameSessionViewHostConfiguration)
+ .isEqualTo(DEFAULT_GAME_SESSION_VIEW_HOST_CONFIGURATION);
+ }
+
+ @Test
+ public void gameTaskStarted_failsToDetermineTaskOverlayConfiguration_gameSessionNotCreated()
+ throws Exception {
+ mGameServiceProviderInstance.start();
+ dispatchTaskCreated(10, GAME_A_MAIN_ACTIVITY);
+
+ mFakeGameService.requestCreateGameSession(10);
+
+ assertThat(mFakeGameSessionService.getCapturedCreateInvocations()).isEmpty();
+ }
+
+ @Test
+ public void gameTaskStartedAndSessionRequested_createsGameSession() throws Exception {
mGameServiceProviderInstance.start();
- ArgumentCaptor<IGameServiceController> controllerArgumentCaptor = ArgumentCaptor.forClass(
- IGameServiceController.class);
- verify(mMockGameService).connected(controllerArgumentCaptor.capture());
- dispatchTaskCreatedAndTriggerSessionRequest(10, GAME_A_MAIN_ACTIVITY,
- controllerArgumentCaptor.getValue());
- IGameSessionStub gameSession10 = new IGameSessionStub();
- gameSession10Future.get().complete(gameSession10);
-
- mInOrder.verify(mMockGameService).connected(any());
- mInOrder.verify(mMockGameService).gameStarted(
- eq(new GameStartedEvent(10, GAME_A_PACKAGE)));
- mInOrder.verify(mMockGameSessionService).create(eq(createGameSessionRequest), any());
- mInOrder.verifyNoMoreInteractions();
+ startTask(10, GAME_A_MAIN_ACTIVITY);
+ mFakeGameService.requestCreateGameSession(10);
+
+ FakeGameSession gameSession10 = new FakeGameSession();
+ SurfacePackage mockSurfacePackage10 = Mockito.mock(SurfacePackage.class);
+ mFakeGameSessionService.removePendingFutureForTaskId(10)
+ .complete(new CreateGameSessionResult(gameSession10, mockSurfacePackage10));
+
assertThat(gameSession10.mIsDestroyed).isFalse();
- assertThat(mFakeGameServiceConnector.getIsConnected()).isTrue();
- assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(1);
- assertThat(mFakeGameSessionServiceConnector.getIsConnected()).isTrue();
- assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(1);
+ assertThat(gameSession10.mIsFocused).isFalse();
}
@Test
public void gameTaskStartedAndSessionRequested_secondSessionRequest_ignoredAndDoesNotCrash()
throws Exception {
- CreateGameSessionRequest createGameSessionRequest =
- new CreateGameSessionRequest(10, GAME_A_PACKAGE);
- Supplier<AndroidFuture<IBinder>> gameSession10Future =
- captureCreateGameSessionFuture(createGameSessionRequest);
+ mGameServiceProviderInstance.start();
+ startTask(10, GAME_A_MAIN_ACTIVITY);
+ mFakeGameService.requestCreateGameSession(10);
+ mFakeGameService.requestCreateGameSession(10);
+
+ CreateGameSessionRequest expectedCreateGameSessionRequest = new CreateGameSessionRequest(10,
+ GAME_A_PACKAGE);
+ assertThat(getOnlyElement(
+ mFakeGameSessionService.getCapturedCreateInvocations()).mCreateGameSessionRequest)
+ .isEqualTo(expectedCreateGameSessionRequest);
+ }
+
+ @Test
+ public void gameSessionSuccessfullyCreated_createsTaskOverlay() throws Exception {
mGameServiceProviderInstance.start();
- ArgumentCaptor<IGameServiceController> controllerArgumentCaptor = ArgumentCaptor.forClass(
- IGameServiceController.class);
- verify(mMockGameService).connected(controllerArgumentCaptor.capture());
- dispatchTaskCreatedAndTriggerSessionRequest(10, GAME_A_MAIN_ACTIVITY,
- controllerArgumentCaptor.getValue());
- IGameSessionStub gameSession10 = new IGameSessionStub();
- gameSession10Future.get().complete(gameSession10);
-
- controllerArgumentCaptor.getValue().createGameSession(10);
-
- mInOrder.verify(mMockGameService).connected(any());
- mInOrder.verify(mMockGameService).gameStarted(
- eq(new GameStartedEvent(10, GAME_A_PACKAGE)));
- mInOrder.verify(mMockGameSessionService).create(eq(createGameSessionRequest), any());
- mInOrder.verifyNoMoreInteractions();
- assertThat(gameSession10.mIsDestroyed).isFalse();
- assertThat(mFakeGameServiceConnector.getIsConnected()).isTrue();
- assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(1);
- assertThat(mFakeGameSessionServiceConnector.getIsConnected()).isTrue();
- assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(1);
+ startTask(10, GAME_A_MAIN_ACTIVITY);
+ mFakeGameService.requestCreateGameSession(10);
+
+ FakeGameSession gameSession10 = new FakeGameSession();
+ SurfacePackage mockSurfacePackage10 = Mockito.mock(SurfacePackage.class);
+ mFakeGameSessionService.removePendingFutureForTaskId(10)
+ .complete(new CreateGameSessionResult(gameSession10, mockSurfacePackage10));
+
+ verify(mMockWindowManagerInternal).addTaskOverlay(eq(10), eq(mockSurfacePackage10));
+ verifyNoMoreInteractions(mMockWindowManagerInternal);
}
@Test
- public void gameTaskRemoved_whileAwaitingGameSessionAttached_destroysGameSession()
+ public void gameTaskFocused_propagatedToGameSession() throws Exception {
+ mGameServiceProviderInstance.start();
+ startTask(10, GAME_A_MAIN_ACTIVITY);
+ mFakeGameService.requestCreateGameSession(10);
+
+ FakeGameSession gameSession10 = new FakeGameSession();
+ SurfacePackage mockSurfacePackage10 = Mockito.mock(SurfacePackage.class);
+ mFakeGameSessionService.removePendingFutureForTaskId(10)
+ .complete(new CreateGameSessionResult(gameSession10, mockSurfacePackage10));
+
+ assertThat(gameSession10.mIsFocused).isFalse();
+
+ dispatchTaskFocused(10, /*focused=*/ true);
+ assertThat(gameSession10.mIsFocused).isTrue();
+
+ dispatchTaskFocused(10, /*focused=*/ false);
+ assertThat(gameSession10.mIsFocused).isFalse();
+ }
+
+ @Test
+ public void gameTaskAlreadyFocusedWhenGameSessionCreated_propagatedToGameSession()
throws Exception {
- CreateGameSessionRequest createGameSessionRequest =
- new CreateGameSessionRequest(10, GAME_A_PACKAGE);
- Supplier<AndroidFuture<IBinder>> gameSession10Future =
- captureCreateGameSessionFuture(createGameSessionRequest);
+ ActivityTaskManager.RootTaskInfo gameATaskInfo = new ActivityTaskManager.RootTaskInfo();
+ gameATaskInfo.taskId = 10;
+ when(mMockActivityTaskManager.getFocusedRootTaskInfo()).thenReturn(gameATaskInfo);
mGameServiceProviderInstance.start();
- ArgumentCaptor<IGameServiceController> controllerArgumentCaptor = ArgumentCaptor.forClass(
- IGameServiceController.class);
- verify(mMockGameService).connected(controllerArgumentCaptor.capture());
- dispatchTaskCreatedAndTriggerSessionRequest(10, GAME_A_MAIN_ACTIVITY,
- controllerArgumentCaptor.getValue());
+ startTask(10, GAME_A_MAIN_ACTIVITY);
+ mFakeGameService.requestCreateGameSession(10);
+
+ FakeGameSession gameSession10 = new FakeGameSession();
+ SurfacePackage mockSurfacePackage10 = Mockito.mock(SurfacePackage.class);
+ mFakeGameSessionService.removePendingFutureForTaskId(10)
+ .complete(new CreateGameSessionResult(gameSession10, mockSurfacePackage10));
+
+ assertThat(gameSession10.mIsFocused).isTrue();
+ }
+
+ @Test
+ public void gameTaskRemoved_whileAwaitingGameSessionAttached_destroysGameSession()
+ throws Exception {
+ mGameServiceProviderInstance.start();
+
+ startTask(10, GAME_A_MAIN_ACTIVITY);
+ mFakeGameService.requestCreateGameSession(10);
+
dispatchTaskRemoved(10);
- IGameSessionStub gameSession10 = new IGameSessionStub();
- gameSession10Future.get().complete(gameSession10);
-
- mInOrder.verify(mMockGameService).connected(any());
- mInOrder.verify(mMockGameService).gameStarted(
- eq(new GameStartedEvent(10, GAME_A_PACKAGE)));
- mInOrder.verify(mMockGameSessionService).create(eq(createGameSessionRequest), any());
- mInOrder.verifyNoMoreInteractions();
+
+ FakeGameSession gameSession10 = new FakeGameSession();
+ SurfacePackage mockSurfacePackage10 = Mockito.mock(SurfacePackage.class);
+ mFakeGameSessionService.removePendingFutureForTaskId(10)
+ .complete(new CreateGameSessionResult(gameSession10, mockSurfacePackage10));
+
assertThat(gameSession10.mIsDestroyed).isTrue();
- assertThat(mFakeGameServiceConnector.getIsConnected()).isTrue();
- assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(1);
- assertThat(mFakeGameSessionServiceConnector.getIsConnected()).isFalse();
- assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(1);
}
@Test
- public void gameTaskRemoved_destroysGameSession() throws Exception {
- CreateGameSessionRequest createGameSessionRequest =
- new CreateGameSessionRequest(10, GAME_A_PACKAGE);
- Supplier<AndroidFuture<IBinder>> gameSession10Future =
- captureCreateGameSessionFuture(createGameSessionRequest);
-
+ public void gameTaskRemoved_whileGameSessionAttached_destroysGameSession() throws Exception {
mGameServiceProviderInstance.start();
- ArgumentCaptor<IGameServiceController> controllerArgumentCaptor = ArgumentCaptor.forClass(
- IGameServiceController.class);
- verify(mMockGameService).connected(controllerArgumentCaptor.capture());
- dispatchTaskCreatedAndTriggerSessionRequest(10, GAME_A_MAIN_ACTIVITY,
- controllerArgumentCaptor.getValue());
- IGameSessionStub gameSession10 = new IGameSessionStub();
- gameSession10Future.get().complete(gameSession10);
+
+ startTask(10, GAME_A_MAIN_ACTIVITY);
+ mFakeGameService.requestCreateGameSession(10);
+
+ FakeGameSession gameSession10 = new FakeGameSession();
+ SurfacePackage mockSurfacePackage10 = Mockito.mock(SurfacePackage.class);
+ mFakeGameSessionService.removePendingFutureForTaskId(10)
+ .complete(new CreateGameSessionResult(gameSession10, mockSurfacePackage10));
+
dispatchTaskRemoved(10);
- mInOrder.verify(mMockGameService).connected(any());
- mInOrder.verify(mMockGameService).gameStarted(
- eq(new GameStartedEvent(10, GAME_A_PACKAGE)));
- mInOrder.verify(mMockGameSessionService).create(eq(createGameSessionRequest), any());
- mInOrder.verifyNoMoreInteractions();
assertThat(gameSession10.mIsDestroyed).isTrue();
- assertThat(mFakeGameServiceConnector.getIsConnected()).isTrue();
- assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(1);
- assertThat(mFakeGameSessionServiceConnector.getIsConnected()).isFalse();
- assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(1);
+ }
+
+ @Test
+ public void gameTaskRemoved_removesTaskOverlay() throws Exception {
+ mGameServiceProviderInstance.start();
+
+ startTask(10, GAME_A_MAIN_ACTIVITY);
+ mFakeGameService.requestCreateGameSession(10);
+
+ FakeGameSession gameSession10 = new FakeGameSession();
+ SurfacePackage mockSurfacePackage10 = Mockito.mock(SurfacePackage.class);
+ mFakeGameSessionService.removePendingFutureForTaskId(10)
+ .complete(new CreateGameSessionResult(gameSession10, mockSurfacePackage10));
+
+ stopTask(10);
+
+ verify(mMockWindowManagerInternal).addTaskOverlay(eq(10), eq(mockSurfacePackage10));
+ verify(mMockWindowManagerInternal).removeTaskOverlay(eq(10), eq(mockSurfacePackage10));
+ verifyNoMoreInteractions(mMockWindowManagerInternal);
}
@Test
public void gameTaskStartedAndSessionRequested_multipleTimes_createsMultipleGameSessions()
throws Exception {
- CreateGameSessionRequest createGameSessionRequest10 =
- new CreateGameSessionRequest(10, GAME_A_PACKAGE);
- Supplier<AndroidFuture<IBinder>> gameSession10Future =
- captureCreateGameSessionFuture(createGameSessionRequest10);
+ mGameServiceProviderInstance.start();
- CreateGameSessionRequest createGameSessionRequest11 =
- new CreateGameSessionRequest(11, GAME_A_PACKAGE);
- Supplier<AndroidFuture<IBinder>> gameSession11Future =
- captureCreateGameSessionFuture(createGameSessionRequest11);
+ startTask(10, GAME_A_MAIN_ACTIVITY);
+ mFakeGameService.requestCreateGameSession(10);
+
+ FakeGameSession gameSession10 = new FakeGameSession();
+ SurfacePackage mockSurfacePackage10 = Mockito.mock(SurfacePackage.class);
+ mFakeGameSessionService.removePendingFutureForTaskId(10)
+ .complete(new CreateGameSessionResult(gameSession10, mockSurfacePackage10));
+
+ startTask(11, GAME_A_MAIN_ACTIVITY);
+ mFakeGameService.requestCreateGameSession(11);
+
+ FakeGameSession gameSession11 = new FakeGameSession();
+ SurfacePackage mockSurfacePackage11 = Mockito.mock(SurfacePackage.class);
+ mFakeGameSessionService.removePendingFutureForTaskId(11)
+ .complete(new CreateGameSessionResult(gameSession11, mockSurfacePackage11));
- mGameServiceProviderInstance.start();
- ArgumentCaptor<IGameServiceController> controllerArgumentCaptor = ArgumentCaptor.forClass(
- IGameServiceController.class);
- verify(mMockGameService).connected(controllerArgumentCaptor.capture());
- dispatchTaskCreatedAndTriggerSessionRequest(10, GAME_A_MAIN_ACTIVITY,
- controllerArgumentCaptor.getValue());
- IGameSessionStub gameSession10 = new IGameSessionStub();
- gameSession10Future.get().complete(gameSession10);
-
- dispatchTaskCreatedAndTriggerSessionRequest(11, GAME_A_MAIN_ACTIVITY,
- controllerArgumentCaptor.getValue());
- IGameSessionStub gameSession11 = new IGameSessionStub();
- gameSession11Future.get().complete(gameSession11);
-
- mInOrder.verify(mMockGameService).connected(any());
- mInOrder.verify(mMockGameService).gameStarted(
- eq(new GameStartedEvent(10, GAME_A_PACKAGE)));
- mInOrder.verify(mMockGameSessionService).create(eq(createGameSessionRequest10), any());
- mInOrder.verify(mMockGameService).gameStarted(
- eq(new GameStartedEvent(11, GAME_A_PACKAGE)));
- mInOrder.verify(mMockGameSessionService).create(eq(createGameSessionRequest11), any());
- mInOrder.verifyNoMoreInteractions();
assertThat(gameSession10.mIsDestroyed).isFalse();
assertThat(gameSession11.mIsDestroyed).isFalse();
- assertThat(mFakeGameServiceConnector.getIsConnected()).isTrue();
- assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(1);
- assertThat(mFakeGameSessionServiceConnector.getIsConnected()).isTrue();
- assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(1);
}
@Test
- public void gameTaskStartedTwice_sessionRequestedSecondTimeOnly_createsOneGameSessions()
+ public void gameTaskStartedTwice_sessionRequestedSecondTimeOnly_createsOneGameSession()
throws Exception {
- CreateGameSessionRequest createGameSessionRequest11 =
- new CreateGameSessionRequest(11, GAME_A_PACKAGE);
- Supplier<AndroidFuture<IBinder>> gameSession11Future =
- captureCreateGameSessionFuture(createGameSessionRequest11);
-
- // The game task is started twice, but a session is requested only for the second one.
mGameServiceProviderInstance.start();
- ArgumentCaptor<IGameServiceController> controllerArgumentCaptor = ArgumentCaptor.forClass(
- IGameServiceController.class);
- verify(mMockGameService).connected(controllerArgumentCaptor.capture());
- dispatchTaskCreated(10, GAME_A_MAIN_ACTIVITY);
- dispatchTaskCreatedAndTriggerSessionRequest(11, GAME_A_MAIN_ACTIVITY,
- controllerArgumentCaptor.getValue());
- IGameSessionStub gameSession11 = new IGameSessionStub();
- gameSession11Future.get().complete(gameSession11);
-
- mInOrder.verify(mMockGameService).connected(any());
- mInOrder.verify(mMockGameService).gameStarted(
- eq(new GameStartedEvent(10, GAME_A_PACKAGE)));
- mInOrder.verify(mMockGameService).gameStarted(
- eq(new GameStartedEvent(11, GAME_A_PACKAGE)));
- mInOrder.verify(mMockGameSessionService).create(eq(createGameSessionRequest11), any());
- mInOrder.verifyNoMoreInteractions();
- assertThat(gameSession11.mIsDestroyed).isFalse();
- assertThat(mFakeGameServiceConnector.getIsConnected()).isTrue();
- assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(1);
- assertThat(mFakeGameSessionServiceConnector.getIsConnected()).isTrue();
- assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(1);
+ startTask(10, GAME_A_MAIN_ACTIVITY);
+ startTask(11, GAME_A_MAIN_ACTIVITY);
+
+ mFakeGameService.requestCreateGameSession(10);
+
+ FakeGameSession gameSession10 = new FakeGameSession();
+ SurfacePackage mockSurfacePackage10 = Mockito.mock(SurfacePackage.class);
+ mFakeGameSessionService.removePendingFutureForTaskId(10)
+ .complete(new CreateGameSessionResult(gameSession10, mockSurfacePackage10));
+
+ assertThat(gameSession10.mIsDestroyed).isFalse();
+ assertThat(mFakeGameSessionService.getCapturedCreateInvocations()).hasSize(1);
}
@Test
- public void gameTaskRemoved_afterMultipleCreated_destroysOnlyThatGameSession()
+ public void gameTaskRemoved_multipleSessions_destroysOnlyThatGameSession()
throws Exception {
- CreateGameSessionRequest createGameSessionRequest10 =
- new CreateGameSessionRequest(10, GAME_A_PACKAGE);
- Supplier<AndroidFuture<IBinder>> gameSession10Future =
- captureCreateGameSessionFuture(createGameSessionRequest10);
+ mGameServiceProviderInstance.start();
- CreateGameSessionRequest createGameSessionRequest11 =
- new CreateGameSessionRequest(11, GAME_A_PACKAGE);
- Supplier<AndroidFuture<IBinder>> gameSession11Future =
- captureCreateGameSessionFuture(createGameSessionRequest11);
+ startTask(10, GAME_A_MAIN_ACTIVITY);
+ mFakeGameService.requestCreateGameSession(10);
- mGameServiceProviderInstance.start();
- ArgumentCaptor<IGameServiceController> controllerArgumentCaptor = ArgumentCaptor.forClass(
- IGameServiceController.class);
- verify(mMockGameService).connected(controllerArgumentCaptor.capture());
- dispatchTaskCreatedAndTriggerSessionRequest(10, GAME_A_MAIN_ACTIVITY,
- controllerArgumentCaptor.getValue());
- IGameSessionStub gameSession10 = new IGameSessionStub();
- gameSession10Future.get().complete(gameSession10);
-
- dispatchTaskCreatedAndTriggerSessionRequest(11, GAME_A_MAIN_ACTIVITY,
- controllerArgumentCaptor.getValue());
- IGameSessionStub gameSession11 = new IGameSessionStub();
- gameSession11Future.get().complete(gameSession11);
+ FakeGameSession gameSession10 = new FakeGameSession();
+ SurfacePackage mockSurfacePackage10 = Mockito.mock(SurfacePackage.class);
+ mFakeGameSessionService.removePendingFutureForTaskId(10)
+ .complete(new CreateGameSessionResult(gameSession10, mockSurfacePackage10));
+
+ startTask(11, GAME_A_MAIN_ACTIVITY);
+ mFakeGameService.requestCreateGameSession(11);
+
+ FakeGameSession gameSession11 = new FakeGameSession();
+ SurfacePackage mockSurfacePackage11 = Mockito.mock(SurfacePackage.class);
+ mFakeGameSessionService.removePendingFutureForTaskId(11)
+ .complete(new CreateGameSessionResult(gameSession11, mockSurfacePackage11));
dispatchTaskRemoved(10);
- mInOrder.verify(mMockGameService).connected(any());
- mInOrder.verify(mMockGameService).gameStarted(
- eq(new GameStartedEvent(10, GAME_A_PACKAGE)));
- mInOrder.verify(mMockGameSessionService).create(eq(createGameSessionRequest10), any());
- mInOrder.verify(mMockGameService).gameStarted(
- eq(new GameStartedEvent(11, GAME_A_PACKAGE)));
- mInOrder.verify(mMockGameSessionService).create(eq(createGameSessionRequest11), any());
- mInOrder.verifyNoMoreInteractions();
assertThat(gameSession10.mIsDestroyed).isTrue();
assertThat(gameSession11.mIsDestroyed).isFalse();
- assertThat(mFakeGameServiceConnector.getIsConnected()).isTrue();
- assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(1);
assertThat(mFakeGameSessionServiceConnector.getIsConnected()).isTrue();
- assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(1);
}
@Test
- public void allGameTasksRemoved_destroysAllGameSessions() throws Exception {
- CreateGameSessionRequest createGameSessionRequest10 =
- new CreateGameSessionRequest(10, GAME_A_PACKAGE);
- Supplier<AndroidFuture<IBinder>> gameSession10Future =
- captureCreateGameSessionFuture(createGameSessionRequest10);
+ public void allGameTasksRemoved_destroysAllGameSessionsAndGameSessionServiceIsDisconnected() {
+ mGameServiceProviderInstance.start();
- CreateGameSessionRequest createGameSessionRequest11 =
- new CreateGameSessionRequest(11, GAME_A_PACKAGE);
- Supplier<AndroidFuture<IBinder>> gameSession11Future =
- captureCreateGameSessionFuture(createGameSessionRequest11);
+ startTask(10, GAME_A_MAIN_ACTIVITY);
+ mFakeGameService.requestCreateGameSession(10);
- mGameServiceProviderInstance.start();
- ArgumentCaptor<IGameServiceController> controllerArgumentCaptor = ArgumentCaptor.forClass(
- IGameServiceController.class);
- verify(mMockGameService).connected(controllerArgumentCaptor.capture());
- dispatchTaskCreatedAndTriggerSessionRequest(10, GAME_A_MAIN_ACTIVITY,
- controllerArgumentCaptor.getValue());
- IGameSessionStub gameSession10 = new IGameSessionStub();
- gameSession10Future.get().complete(gameSession10);
-
- dispatchTaskCreatedAndTriggerSessionRequest(11, GAME_A_MAIN_ACTIVITY,
- controllerArgumentCaptor.getValue());
- IGameSessionStub gameSession11 = new IGameSessionStub();
- gameSession11Future.get().complete(gameSession11);
+ FakeGameSession gameSession10 = new FakeGameSession();
+ SurfacePackage mockSurfacePackage10 = Mockito.mock(SurfacePackage.class);
+ mFakeGameSessionService.removePendingFutureForTaskId(10)
+ .complete(new CreateGameSessionResult(gameSession10, mockSurfacePackage10));
+
+ startTask(11, GAME_A_MAIN_ACTIVITY);
+ mFakeGameService.requestCreateGameSession(11);
+
+ FakeGameSession gameSession11 = new FakeGameSession();
+ SurfacePackage mockSurfacePackage11 = Mockito.mock(SurfacePackage.class);
+ mFakeGameSessionService.removePendingFutureForTaskId(11)
+ .complete(new CreateGameSessionResult(gameSession11, mockSurfacePackage11));
dispatchTaskRemoved(10);
dispatchTaskRemoved(11);
- mInOrder.verify(mMockGameService).connected(any());
- mInOrder.verify(mMockGameService).gameStarted(
- eq(new GameStartedEvent(10, GAME_A_PACKAGE)));
- mInOrder.verify(mMockGameSessionService).create(eq(createGameSessionRequest10), any());
- mInOrder.verify(mMockGameService).gameStarted(
- eq(new GameStartedEvent(11, GAME_A_PACKAGE)));
- mInOrder.verify(mMockGameSessionService).create(eq(createGameSessionRequest11), any());
- mInOrder.verifyNoMoreInteractions();
assertThat(gameSession10.mIsDestroyed).isTrue();
assertThat(gameSession11.mIsDestroyed).isTrue();
- assertThat(mFakeGameServiceConnector.getIsConnected()).isTrue();
- assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(1);
assertThat(mFakeGameSessionServiceConnector.getIsConnected()).isFalse();
- assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(1);
}
@Test
- public void gameTasksCreatedAndSessionsReq_afterAllPreviousSessionsDestroyed_createsSession()
+ public void createSessionRequested_afterAllPreviousSessionsDestroyed_createsSession()
throws Exception {
- CreateGameSessionRequest createGameSessionRequest10 =
- new CreateGameSessionRequest(10, GAME_A_PACKAGE);
- Supplier<AndroidFuture<IBinder>> gameSession10Future =
- captureCreateGameSessionFuture(createGameSessionRequest10);
+ mGameServiceProviderInstance.start();
- CreateGameSessionRequest createGameSessionRequest11 =
- new CreateGameSessionRequest(11, GAME_A_PACKAGE);
- Supplier<AndroidFuture<IBinder>> gameSession11Future =
- captureCreateGameSessionFuture(createGameSessionRequest11);
+ startTask(10, GAME_A_MAIN_ACTIVITY);
+ mFakeGameService.requestCreateGameSession(10);
- CreateGameSessionRequest createGameSessionRequest12 =
- new CreateGameSessionRequest(12, GAME_A_PACKAGE);
- Supplier<AndroidFuture<IBinder>> unusedGameSession12Future =
- captureCreateGameSessionFuture(createGameSessionRequest12);
+ FakeGameSession gameSession10 = new FakeGameSession();
+ SurfacePackage mockSurfacePackage10 = Mockito.mock(SurfacePackage.class);
+ mFakeGameSessionService.removePendingFutureForTaskId(10)
+ .complete(new CreateGameSessionResult(gameSession10, mockSurfacePackage10));
- mGameServiceProviderInstance.start();
- ArgumentCaptor<IGameServiceController> controllerArgumentCaptor = ArgumentCaptor.forClass(
- IGameServiceController.class);
- verify(mMockGameService).connected(controllerArgumentCaptor.capture());
- dispatchTaskCreatedAndTriggerSessionRequest(10, GAME_A_MAIN_ACTIVITY,
- controllerArgumentCaptor.getValue());
- IGameSessionStub gameSession10 = new IGameSessionStub();
- gameSession10Future.get().complete(gameSession10);
-
- dispatchTaskCreatedAndTriggerSessionRequest(11, GAME_A_MAIN_ACTIVITY,
- controllerArgumentCaptor.getValue());
- IGameSessionStub gameSession11 = new IGameSessionStub();
- gameSession11Future.get().complete(gameSession11);
+ startTask(11, GAME_A_MAIN_ACTIVITY);
+ mFakeGameService.requestCreateGameSession(11);
+
+ FakeGameSession gameSession11 = new FakeGameSession();
+ SurfacePackage mockSurfacePackage11 = Mockito.mock(SurfacePackage.class);
+ mFakeGameSessionService.removePendingFutureForTaskId(11)
+ .complete(new CreateGameSessionResult(gameSession11, mockSurfacePackage11));
dispatchTaskRemoved(10);
dispatchTaskRemoved(11);
- dispatchTaskCreatedAndTriggerSessionRequest(12, GAME_A_MAIN_ACTIVITY,
- controllerArgumentCaptor.getValue());
- IGameSessionStub gameSession12 = new IGameSessionStub();
- gameSession11Future.get().complete(gameSession12);
-
- mInOrder.verify(mMockGameService).connected(any());
- mInOrder.verify(mMockGameService).gameStarted(
- eq(new GameStartedEvent(10, GAME_A_PACKAGE)));
- mInOrder.verify(mMockGameSessionService).create(eq(createGameSessionRequest10), any());
- mInOrder.verify(mMockGameService).gameStarted(
- eq(new GameStartedEvent(11, GAME_A_PACKAGE)));
- mInOrder.verify(mMockGameSessionService).create(eq(createGameSessionRequest11), any());
- mInOrder.verify(mMockGameService).gameStarted(
- eq(new GameStartedEvent(12, GAME_A_PACKAGE)));
- mInOrder.verify(mMockGameSessionService).create(eq(createGameSessionRequest12), any());
- mInOrder.verifyNoMoreInteractions();
+ startTask(12, GAME_A_MAIN_ACTIVITY);
+ mFakeGameService.requestCreateGameSession(12);
+
+ FakeGameSession gameSession12 = new FakeGameSession();
+ SurfacePackage mockSurfacePackage12 = Mockito.mock(SurfacePackage.class);
+ mFakeGameSessionService.removePendingFutureForTaskId(12)
+ .complete(new CreateGameSessionResult(gameSession12, mockSurfacePackage12));
+
assertThat(gameSession10.mIsDestroyed).isTrue();
assertThat(gameSession11.mIsDestroyed).isTrue();
assertThat(gameSession12.mIsDestroyed).isFalse();
- assertThat(mFakeGameServiceConnector.getIsConnected()).isTrue();
- assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(1);
assertThat(mFakeGameSessionServiceConnector.getIsConnected()).isTrue();
- assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(2);
}
@Test
public void stop_severalActiveGameSessions_destroysGameSessionsAndUnbinds() throws Exception {
- CreateGameSessionRequest createGameSessionRequest10 =
- new CreateGameSessionRequest(10, GAME_A_PACKAGE);
- Supplier<AndroidFuture<IBinder>> gameSession10Future =
- captureCreateGameSessionFuture(createGameSessionRequest10);
+ mGameServiceProviderInstance.start();
- CreateGameSessionRequest createGameSessionRequest11 =
- new CreateGameSessionRequest(11, GAME_A_PACKAGE);
- Supplier<AndroidFuture<IBinder>> gameSession11Future =
- captureCreateGameSessionFuture(createGameSessionRequest11);
+ startTask(10, GAME_A_MAIN_ACTIVITY);
+ mFakeGameService.requestCreateGameSession(10);
+
+ FakeGameSession gameSession10 = new FakeGameSession();
+ SurfacePackage mockSurfacePackage10 = Mockito.mock(SurfacePackage.class);
+ mFakeGameSessionService.removePendingFutureForTaskId(10)
+ .complete(new CreateGameSessionResult(gameSession10, mockSurfacePackage10));
+
+ startTask(11, GAME_A_MAIN_ACTIVITY);
+ mFakeGameService.requestCreateGameSession(11);
+
+ FakeGameSession gameSession11 = new FakeGameSession();
+ SurfacePackage mockSurfacePackage11 = Mockito.mock(SurfacePackage.class);
+ mFakeGameSessionService.removePendingFutureForTaskId(11)
+ .complete(new CreateGameSessionResult(gameSession11, mockSurfacePackage11));
- mGameServiceProviderInstance.start();
- ArgumentCaptor<IGameServiceController> controllerArgumentCaptor = ArgumentCaptor.forClass(
- IGameServiceController.class);
- verify(mMockGameService).connected(controllerArgumentCaptor.capture());
- dispatchTaskCreatedAndTriggerSessionRequest(10, GAME_A_MAIN_ACTIVITY,
- controllerArgumentCaptor.getValue());
- IGameSessionStub gameSession10 = new IGameSessionStub();
- gameSession10Future.get().complete(gameSession10);
- dispatchTaskCreatedAndTriggerSessionRequest(11, GAME_A_MAIN_ACTIVITY,
- controllerArgumentCaptor.getValue());
- IGameSessionStub gameSession11 = new IGameSessionStub();
- gameSession11Future.get().complete(gameSession11);
mGameServiceProviderInstance.stop();
- mInOrder.verify(mMockGameService).connected(any());
- mInOrder.verify(mMockGameService).gameStarted(
- eq(new GameStartedEvent(10, GAME_A_PACKAGE)));
- mInOrder.verify(mMockGameSessionService).create(eq(createGameSessionRequest10), any());
- mInOrder.verify(mMockGameService).gameStarted(
- eq(new GameStartedEvent(11, GAME_A_PACKAGE)));
- mInOrder.verify(mMockGameSessionService).create(eq(createGameSessionRequest11), any());
- mInOrder.verify(mMockGameService).disconnected();
- mInOrder.verifyNoMoreInteractions();
assertThat(gameSession10.mIsDestroyed).isTrue();
assertThat(gameSession11.mIsDestroyed).isTrue();
assertThat(mFakeGameServiceConnector.getIsConnected()).isFalse();
- assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(1);
assertThat(mFakeGameSessionServiceConnector.getIsConnected()).isFalse();
- assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(1);
}
- private Supplier<AndroidFuture<IBinder>> captureCreateGameSessionFuture(
- CreateGameSessionRequest expectedCreateGameSessionRequest) throws Exception {
- final AtomicReference<AndroidFuture<IBinder>> gameSessionFuture = new AtomicReference<>();
- doAnswer(invocation -> {
- gameSessionFuture.set(invocation.getArgument(1));
- return null;
- }).when(mMockGameSessionService).create(eq(expectedCreateGameSessionRequest), any());
+ @Test
+ public void takeScreenshot_failureNoBitmapCaptured() throws Exception {
+ mGameServiceProviderInstance.start();
+ startTask(10, GAME_A_MAIN_ACTIVITY);
+ mFakeGameService.requestCreateGameSession(10);
+
+ IGameSessionController gameSessionController = getOnlyElement(
+ mFakeGameSessionService.getCapturedCreateInvocations()).mGameSessionController;
+ AndroidFuture<GameScreenshotResult> resultFuture = new AndroidFuture<>();
+ gameSessionController.takeScreenshot(10, resultFuture);
+
+ GameScreenshotResult result = resultFuture.get();
+ assertEquals(GameScreenshotResult.GAME_SCREENSHOT_ERROR_INTERNAL_ERROR,
+ result.getStatus());
+ verify(mMockWindowManagerService).captureTaskBitmap(10);
+ }
+
+ @Test
+ public void takeScreenshot_success() throws Exception {
+ when(mMockWindowManagerService.captureTaskBitmap(10)).thenReturn(TEST_BITMAP);
+
+ mGameServiceProviderInstance.start();
+ startTask(10, GAME_A_MAIN_ACTIVITY);
+ mFakeGameService.requestCreateGameSession(10);
+
+ IGameSessionController gameSessionController = getOnlyElement(
+ mFakeGameSessionService.getCapturedCreateInvocations()).mGameSessionController;
+ AndroidFuture<GameScreenshotResult> resultFuture = new AndroidFuture<>();
+ gameSessionController.takeScreenshot(10, resultFuture);
+
+ GameScreenshotResult result = resultFuture.get();
+ assertEquals(GameScreenshotResult.GAME_SCREENSHOT_SUCCESS, result.getStatus());
+ assertEquals(TEST_BITMAP, result.getBitmap());
+ }
+
+ private void startTask(int taskId, ComponentName componentName) {
+ RunningTaskInfo runningTaskInfo = new RunningTaskInfo();
+ runningTaskInfo.taskId = taskId;
+ runningTaskInfo.displayId = 1;
+ runningTaskInfo.configuration.windowConfiguration.setBounds(new Rect(0, 0, 500, 800));
+ mRunningTaskInfos.add(runningTaskInfo);
+
+ dispatchTaskCreated(taskId, componentName);
+ }
- return gameSessionFuture::get;
+ private void stopTask(int taskId) {
+ mRunningTaskInfos.removeIf(runningTaskInfo -> runningTaskInfo.taskId == taskId);
+ dispatchTaskRemoved(taskId);
}
+
private void dispatchTaskRemoved(int taskId) {
dispatchTaskChangeEvent(taskStackListener -> {
taskStackListener.onTaskRemoved(taskId);
});
}
- private void dispatchTaskCreatedAndTriggerSessionRequest(int taskId,
- @Nullable ComponentName componentName, IGameServiceController gameServiceController)
- throws Exception {
- dispatchTaskCreated(taskId, componentName);
- gameServiceController.createGameSession(taskId);
- }
-
private void dispatchTaskCreated(int taskId, @Nullable ComponentName componentName) {
dispatchTaskChangeEvent(taskStackListener -> {
taskStackListener.onTaskCreated(taskId, componentName);
});
}
+ private void dispatchTaskFocused(int taskId, boolean focused) {
+ dispatchTaskChangeEvent(taskStackListener -> {
+ taskStackListener.onTaskFocusChanged(taskId, focused);
+ });
+ }
+
private void dispatchTaskChangeEvent(
ThrowingConsumer<ITaskStackListener> taskStackListenerConsumer) {
for (ITaskStackListener taskStackListener : mTaskStackListeners) {
@@ -721,12 +701,129 @@ public final class GameServiceProviderInstanceImplTest {
}
}
- private static class IGameSessionStub extends IGameSession.Stub {
+ static final class FakeGameService extends IGameService.Stub {
+ private IGameServiceController mGameServiceController;
+
+ public enum GameServiceState {
+ DISCONNECTED,
+ CONNECTED,
+ }
+
+ private ArrayList<GameStartedEvent> mGameStartedEvents = new ArrayList<>();
+ private int mConnectedCount = 0;
+ private GameServiceState mGameServiceState = GameServiceState.DISCONNECTED;
+
+ public GameServiceState getState() {
+ return mGameServiceState;
+ }
+
+ public int getConnectedCount() {
+ return mConnectedCount;
+ }
+
+ public ArrayList<GameStartedEvent> getGameStartedEvents() {
+ return mGameStartedEvents;
+ }
+
+ @Override
+ public void connected(IGameServiceController gameServiceController) {
+ Preconditions.checkState(mGameServiceState == GameServiceState.DISCONNECTED);
+
+ mGameServiceState = GameServiceState.CONNECTED;
+ mConnectedCount += 1;
+ mGameServiceController = gameServiceController;
+ }
+
+ @Override
+ public void disconnected() {
+ Preconditions.checkState(mGameServiceState == GameServiceState.CONNECTED);
+
+ mGameServiceState = GameServiceState.DISCONNECTED;
+ mGameServiceController = null;
+ }
+
+ @Override
+ public void gameStarted(GameStartedEvent gameStartedEvent) {
+ Preconditions.checkState(mGameServiceState == GameServiceState.CONNECTED);
+
+ mGameStartedEvents.add(gameStartedEvent);
+ }
+
+ public void requestCreateGameSession(int task) {
+ Preconditions.checkState(mGameServiceState == GameServiceState.CONNECTED);
+
+ try {
+ mGameServiceController.createGameSession(task);
+ } catch (RemoteException ex) {
+ throw new AssertionError(ex);
+ }
+ }
+ }
+
+ static final class FakeGameSessionService extends IGameSessionService.Stub {
+
+ private final ArrayList<CapturedCreateInvocation> mCapturedCreateInvocations =
+ new ArrayList<>();
+ private final HashMap<Integer, AndroidFuture<CreateGameSessionResult>>
+ mPendingCreateGameSessionResultFutures =
+ new HashMap<>();
+
+ public static final class CapturedCreateInvocation {
+ private final IGameSessionController mGameSessionController;
+ private final CreateGameSessionRequest mCreateGameSessionRequest;
+ private final GameSessionViewHostConfiguration mGameSessionViewHostConfiguration;
+
+ CapturedCreateInvocation(
+ IGameSessionController gameSessionController,
+ CreateGameSessionRequest createGameSessionRequest,
+ GameSessionViewHostConfiguration gameSessionViewHostConfiguration) {
+ mGameSessionController = gameSessionController;
+ mCreateGameSessionRequest = createGameSessionRequest;
+ mGameSessionViewHostConfiguration = gameSessionViewHostConfiguration;
+ }
+ }
+
+ public ArrayList<CapturedCreateInvocation> getCapturedCreateInvocations() {
+ return mCapturedCreateInvocations;
+ }
+
+ public AndroidFuture<CreateGameSessionResult> removePendingFutureForTaskId(int taskId) {
+ return mPendingCreateGameSessionResultFutures.remove(taskId);
+ }
+
+ @Override
+ public void create(
+ IGameSessionController gameSessionController,
+ CreateGameSessionRequest createGameSessionRequest,
+ GameSessionViewHostConfiguration gameSessionViewHostConfiguration,
+ AndroidFuture createGameSessionResultFuture) {
+
+ mCapturedCreateInvocations.add(
+ new CapturedCreateInvocation(
+ gameSessionController,
+ createGameSessionRequest,
+ gameSessionViewHostConfiguration));
+
+ Preconditions.checkState(!mPendingCreateGameSessionResultFutures.containsKey(
+ createGameSessionRequest.getTaskId()));
+ mPendingCreateGameSessionResultFutures.put(
+ createGameSessionRequest.getTaskId(),
+ createGameSessionResultFuture);
+ }
+ }
+
+ private static class FakeGameSession extends IGameSession.Stub {
boolean mIsDestroyed = false;
+ boolean mIsFocused = false;
@Override
- public void destroy() {
+ public void onDestroyed() {
mIsDestroyed = true;
}
+
+ @Override
+ public void onTaskFocusChanged(boolean focused) {
+ mIsFocused = focused;
+ }
}
-}
+} \ No newline at end of file
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java
index d7414591f83c..8a954caad939 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java
@@ -383,6 +383,10 @@ public class PrefetchControllerTest {
inOrder.verify(mUsageStatsManagerInternal, timeout(DEFAULT_WAIT_MS).times(0))
.getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID);
+ verify(mAlarmManager, timeout(DEFAULT_WAIT_MS).times(1))
+ .setWindow(
+ anyInt(), eq(sElapsedRealtimeClock.millis() + 3 * HOUR_IN_MILLIS),
+ anyLong(), eq(TAG_PREFETCH), any(), any());
assertFalse(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH));
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
index cfae9a3d586a..153ce17ec9dd 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
@@ -16,6 +16,11 @@
package com.android.server.job.controllers;
+import static android.app.job.JobInfo.PRIORITY_DEFAULT;
+import static android.app.job.JobInfo.PRIORITY_HIGH;
+import static android.app.job.JobInfo.PRIORITY_LOW;
+import static android.app.job.JobInfo.PRIORITY_MIN;
+
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -269,14 +274,14 @@ public class QuotaControllerTest {
}
private void setCharging() {
- doReturn(true).when(mJobSchedulerService).isBatteryCharging();
+ when(mJobSchedulerService.isBatteryCharging()).thenReturn(true);
synchronized (mQuotaController.mLock) {
mQuotaController.onBatteryStateChangedLocked();
}
}
private void setDischarging() {
- doReturn(false).when(mJobSchedulerService).isBatteryCharging();
+ when(mJobSchedulerService.isBatteryCharging()).thenReturn(false);
synchronized (mQuotaController.mLock) {
mQuotaController.onBatteryStateChangedLocked();
}
@@ -407,6 +412,14 @@ public class QuotaControllerTest {
}
}
+ private void setDeviceConfigFloat(String key, float val) {
+ mDeviceConfigPropertiesBuilder.setFloat(key, val);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.prepareForUpdatedConstantsLocked();
+ mQcConstants.processConstantLocked(mDeviceConfigPropertiesBuilder.build(), key);
+ }
+ }
+
private void waitForNonDelayedMessagesProcessed() {
mQuotaController.getHandler().runWithScissors(() -> {}, 15_000);
}
@@ -839,7 +852,7 @@ public class QuotaControllerTest {
SOURCE_USER_ID, SOURCE_PACKAGE, inputStats);
assertEquals(expectedStats, inputStats);
assertTrue(mQuotaController.isWithinQuotaLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, RARE_INDEX));
+ SOURCE_USER_ID, SOURCE_PACKAGE, RARE_INDEX, PRIORITY_DEFAULT));
}
assertTrue("Job not ready: " + jobStatus, jobStatus.isReady());
}
@@ -863,7 +876,7 @@ public class QuotaControllerTest {
assertEquals(expectedStats, inputStats);
assertFalse(
mQuotaController.isWithinQuotaLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, RARE_INDEX));
+ SOURCE_USER_ID, SOURCE_PACKAGE, RARE_INDEX, PRIORITY_DEFAULT));
}
// Quota should be exceeded due to activity in active timer.
@@ -888,7 +901,7 @@ public class QuotaControllerTest {
assertEquals(expectedStats, inputStats);
assertFalse(
mQuotaController.isWithinQuotaLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, RARE_INDEX));
+ SOURCE_USER_ID, SOURCE_PACKAGE, RARE_INDEX, PRIORITY_DEFAULT));
assertFalse("Job unexpectedly ready: " + jobStatus, jobStatus.isReady());
}
}
@@ -1484,7 +1497,7 @@ public class QuotaControllerTest {
SOURCE_USER_ID, SOURCE_PACKAGE));
assertEquals(MINUTE_IN_MILLIS,
mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE));
+ SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT));
}
setStandbyBucket(FREQUENT_INDEX);
@@ -1494,7 +1507,7 @@ public class QuotaControllerTest {
SOURCE_USER_ID, SOURCE_PACKAGE));
assertEquals(MINUTE_IN_MILLIS,
mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE));
+ SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT));
}
setStandbyBucket(WORKING_INDEX);
@@ -1504,7 +1517,7 @@ public class QuotaControllerTest {
SOURCE_USER_ID, SOURCE_PACKAGE));
assertEquals(7 * MINUTE_IN_MILLIS,
mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE));
+ SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT));
}
// ACTIVE window = allowed time, so jobs can essentially run non-stop until they reach the
@@ -1516,7 +1529,7 @@ public class QuotaControllerTest {
SOURCE_USER_ID, SOURCE_PACKAGE));
assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 9 * MINUTE_IN_MILLIS,
mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE));
+ SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT));
}
}
@@ -1540,7 +1553,7 @@ public class QuotaControllerTest {
// Max time will phase out, so should use bucket limit.
assertEquals(10 * MINUTE_IN_MILLIS,
mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE));
+ SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT));
}
mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE).clear();
@@ -1556,7 +1569,7 @@ public class QuotaControllerTest {
SOURCE_USER_ID, SOURCE_PACKAGE));
assertEquals(10 * MINUTE_IN_MILLIS,
mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE));
+ SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT));
}
mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE).clear();
@@ -1573,7 +1586,7 @@ public class QuotaControllerTest {
SOURCE_USER_ID, SOURCE_PACKAGE));
assertEquals(3 * MINUTE_IN_MILLIS,
mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE));
+ SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT));
}
}
@@ -1606,7 +1619,7 @@ public class QuotaControllerTest {
// window time.
assertEquals(10 * MINUTE_IN_MILLIS,
mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE));
+ SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT));
}
mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE).clear();
@@ -1633,15 +1646,115 @@ public class QuotaControllerTest {
// Max time only has one minute phase out. Bucket time has 2 minute phase out.
assertEquals(9 * MINUTE_IN_MILLIS,
mQuotaController.getTimeUntilQuotaConsumedLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT));
+ }
+ }
+
+ /**
+ * Test getTimeUntilQuotaConsumedLocked when the determination is based on the job's priority.
+ */
+ @Test
+ public void testGetTimeUntilQuotaConsumedLocked_Priority() {
+ final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+ // Close to RARE boundary.
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - (24 * HOUR_IN_MILLIS - 30 * SECOND_IN_MILLIS),
+ 150 * SECOND_IN_MILLIS, 5), false);
+ // Far away from FREQUENT boundary.
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - (7 * HOUR_IN_MILLIS), 2 * MINUTE_IN_MILLIS, 5), false);
+ // Overlap WORKING_SET boundary.
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - (2 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS),
+ 2 * MINUTE_IN_MILLIS, 5), false);
+ // Close to ACTIVE boundary.
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - (9 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false);
+
+ setStandbyBucket(RARE_INDEX);
+ synchronized (mQuotaController.mLock) {
+ assertEquals(30 * SECOND_IN_MILLIS,
+ mQuotaController.getRemainingExecutionTimeLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+ assertEquals(3 * MINUTE_IN_MILLIS,
+ mQuotaController.getTimeUntilQuotaConsumedLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_HIGH));
+ assertEquals(3 * MINUTE_IN_MILLIS,
+ mQuotaController.getTimeUntilQuotaConsumedLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT));
+ assertEquals(0,
+ mQuotaController.getTimeUntilQuotaConsumedLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_LOW));
+ assertEquals(0,
+ mQuotaController.getTimeUntilQuotaConsumedLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_MIN));
+ }
+
+ setStandbyBucket(FREQUENT_INDEX);
+ synchronized (mQuotaController.mLock) {
+ assertEquals(3 * MINUTE_IN_MILLIS,
+ mQuotaController.getRemainingExecutionTimeLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+ assertEquals(3 * MINUTE_IN_MILLIS,
+ mQuotaController.getTimeUntilQuotaConsumedLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_HIGH));
+ assertEquals(3 * MINUTE_IN_MILLIS,
+ mQuotaController.getTimeUntilQuotaConsumedLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT));
+ assertEquals(30 * SECOND_IN_MILLIS,
+ mQuotaController.getTimeUntilQuotaConsumedLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_LOW));
+ assertEquals(0,
+ mQuotaController.getTimeUntilQuotaConsumedLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_MIN));
+ }
+
+ setStandbyBucket(WORKING_INDEX);
+ synchronized (mQuotaController.mLock) {
+ assertEquals(6 * MINUTE_IN_MILLIS,
+ mQuotaController.getRemainingExecutionTimeLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+ assertEquals(7 * MINUTE_IN_MILLIS,
+ mQuotaController.getTimeUntilQuotaConsumedLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_HIGH));
+ assertEquals(7 * MINUTE_IN_MILLIS,
+ mQuotaController.getTimeUntilQuotaConsumedLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT));
+ assertEquals(4 * MINUTE_IN_MILLIS + 30 * SECOND_IN_MILLIS,
+ mQuotaController.getTimeUntilQuotaConsumedLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_LOW));
+ assertEquals(2 * MINUTE_IN_MILLIS,
+ mQuotaController.getTimeUntilQuotaConsumedLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_MIN));
+ }
+
+ // ACTIVE window = allowed time, so jobs can essentially run non-stop until they reach the
+ // max execution time.
+ setStandbyBucket(ACTIVE_INDEX);
+ synchronized (mQuotaController.mLock) {
+ assertEquals(7 * MINUTE_IN_MILLIS,
+ mQuotaController.getRemainingExecutionTimeLocked(
SOURCE_USER_ID, SOURCE_PACKAGE));
+ assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 7 * MINUTE_IN_MILLIS,
+ mQuotaController.getTimeUntilQuotaConsumedLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_HIGH));
+ assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 7 * MINUTE_IN_MILLIS,
+ mQuotaController.getTimeUntilQuotaConsumedLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT));
+ assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 7 * MINUTE_IN_MILLIS,
+ mQuotaController.getTimeUntilQuotaConsumedLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_LOW));
+ assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 7 * MINUTE_IN_MILLIS,
+ mQuotaController.getTimeUntilQuotaConsumedLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_MIN));
}
}
@Test
public void testIsWithinQuotaLocked_NeverApp() {
synchronized (mQuotaController.mLock) {
- assertFalse(
- mQuotaController.isWithinQuotaLocked(0, "com.android.test.never", NEVER_INDEX));
+ assertFalse(mQuotaController.isWithinQuotaLocked(
+ 0, "com.android.test.never", NEVER_INDEX, PRIORITY_DEFAULT));
}
}
@@ -1649,7 +1762,8 @@ public class QuotaControllerTest {
public void testIsWithinQuotaLocked_Charging() {
setCharging();
synchronized (mQuotaController.mLock) {
- assertTrue(mQuotaController.isWithinQuotaLocked(0, "com.android.test", RARE_INDEX));
+ assertTrue(mQuotaController.isWithinQuotaLocked(
+ 0, "com.android.test", RARE_INDEX, PRIORITY_DEFAULT));
}
}
@@ -1663,7 +1777,8 @@ public class QuotaControllerTest {
createTimingSession(now - (5 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false);
synchronized (mQuotaController.mLock) {
mQuotaController.incrementJobCountLocked(0, "com.android.test", 5);
- assertTrue(mQuotaController.isWithinQuotaLocked(0, "com.android.test", WORKING_INDEX));
+ assertTrue(mQuotaController.isWithinQuotaLocked(
+ 0, "com.android.test", WORKING_INDEX, PRIORITY_DEFAULT));
}
}
@@ -1680,7 +1795,7 @@ public class QuotaControllerTest {
synchronized (mQuotaController.mLock) {
mQuotaController.incrementJobCountLocked(0, "com.android.test.spam", jobCount);
assertFalse(mQuotaController.isWithinQuotaLocked(
- 0, "com.android.test.spam", WORKING_INDEX));
+ 0, "com.android.test.spam", WORKING_INDEX, PRIORITY_DEFAULT));
}
mQuotaController.saveTimingSession(0, "com.android.test.frequent",
@@ -1690,7 +1805,7 @@ public class QuotaControllerTest {
createTimingSession(now - (HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 500), false);
synchronized (mQuotaController.mLock) {
assertFalse(mQuotaController.isWithinQuotaLocked(
- 0, "com.android.test.frequent", FREQUENT_INDEX));
+ 0, "com.android.test.frequent", FREQUENT_INDEX, PRIORITY_DEFAULT));
}
}
@@ -1706,7 +1821,8 @@ public class QuotaControllerTest {
createTimingSession(now - (5 * MINUTE_IN_MILLIS), 4 * MINUTE_IN_MILLIS, 5), false);
synchronized (mQuotaController.mLock) {
mQuotaController.incrementJobCountLocked(0, "com.android.test", 5);
- assertFalse(mQuotaController.isWithinQuotaLocked(0, "com.android.test", WORKING_INDEX));
+ assertFalse(mQuotaController.isWithinQuotaLocked(
+ 0, "com.android.test", WORKING_INDEX, PRIORITY_DEFAULT));
}
}
@@ -1722,7 +1838,8 @@ public class QuotaControllerTest {
false);
synchronized (mQuotaController.mLock) {
mQuotaController.incrementJobCountLocked(0, "com.android.test", jobCount);
- assertFalse(mQuotaController.isWithinQuotaLocked(0, "com.android.test", WORKING_INDEX));
+ assertFalse(mQuotaController.isWithinQuotaLocked(
+ 0, "com.android.test", WORKING_INDEX, PRIORITY_DEFAULT));
}
}
@@ -1875,22 +1992,66 @@ public class QuotaControllerTest {
assertEquals("Rare has incorrect quota status with " + (i + 1) + " sessions",
i < 2,
- mQuotaController.isWithinQuotaLocked(0, "com.android.test", RARE_INDEX));
+ mQuotaController.isWithinQuotaLocked(
+ 0, "com.android.test", RARE_INDEX, PRIORITY_DEFAULT));
assertEquals("Frequent has incorrect quota status with " + (i + 1) + " sessions",
i < 3,
mQuotaController.isWithinQuotaLocked(
- 0, "com.android.test", FREQUENT_INDEX));
+ 0, "com.android.test", FREQUENT_INDEX, PRIORITY_DEFAULT));
assertEquals("Working has incorrect quota status with " + (i + 1) + " sessions",
i < 4,
- mQuotaController.isWithinQuotaLocked(0, "com.android.test", WORKING_INDEX));
+ mQuotaController.isWithinQuotaLocked(
+ 0, "com.android.test", WORKING_INDEX, PRIORITY_DEFAULT));
assertEquals("Active has incorrect quota status with " + (i + 1) + " sessions",
i < 5,
- mQuotaController.isWithinQuotaLocked(0, "com.android.test", ACTIVE_INDEX));
+ mQuotaController.isWithinQuotaLocked(
+ 0, "com.android.test", ACTIVE_INDEX, PRIORITY_DEFAULT));
}
}
}
@Test
+ public void testIsWithinQuotaLocked_Priority() {
+ setDischarging();
+ final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(now - (7 * HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false);
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(now - (HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false);
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(now - (5 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.incrementJobCountLocked(0, "com.android.test", 5);
+ assertTrue(mQuotaController.isWithinQuotaLocked(
+ 0, "com.android.test", FREQUENT_INDEX, PRIORITY_HIGH));
+ assertTrue(mQuotaController.isWithinQuotaLocked(
+ 0, "com.android.test", FREQUENT_INDEX, PRIORITY_DEFAULT));
+ assertFalse(mQuotaController.isWithinQuotaLocked(
+ 0, "com.android.test", FREQUENT_INDEX, PRIORITY_LOW));
+ assertFalse(mQuotaController.isWithinQuotaLocked(
+ 0, "com.android.test", FREQUENT_INDEX, PRIORITY_MIN));
+
+ assertTrue(mQuotaController.isWithinQuotaLocked(
+ 0, "com.android.test", WORKING_INDEX, PRIORITY_HIGH));
+ assertTrue(mQuotaController.isWithinQuotaLocked(
+ 0, "com.android.test", WORKING_INDEX, PRIORITY_DEFAULT));
+ assertTrue(mQuotaController.isWithinQuotaLocked(
+ 0, "com.android.test", WORKING_INDEX, PRIORITY_LOW));
+ assertFalse(mQuotaController.isWithinQuotaLocked(
+ 0, "com.android.test", WORKING_INDEX, PRIORITY_MIN));
+
+ assertTrue(mQuotaController.isWithinQuotaLocked(
+ 0, "com.android.test", ACTIVE_INDEX, PRIORITY_HIGH));
+ assertTrue(mQuotaController.isWithinQuotaLocked(
+ 0, "com.android.test", ACTIVE_INDEX, PRIORITY_DEFAULT));
+ assertTrue(mQuotaController.isWithinQuotaLocked(
+ 0, "com.android.test", ACTIVE_INDEX, PRIORITY_LOW));
+ assertTrue(mQuotaController.isWithinQuotaLocked(
+ 0, "com.android.test", ACTIVE_INDEX, PRIORITY_MIN));
+ }
+ }
+
+ @Test
public void testIsWithinEJQuotaLocked_NeverApp() {
JobStatus js = createExpeditedJobStatus("testIsWithinEJQuotaLocked_NeverApp", 1);
setStandbyBucket(NEVER_INDEX, js);
@@ -2116,6 +2277,12 @@ public class QuotaControllerTest {
final int standbyBucket = ACTIVE_INDEX;
setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);
+ JobStatus jobStatus = createJobStatus("testMaybeScheduleStartAlarmLocked_Active", 1);
+ setStandbyBucket(standbyBucket, jobStatus);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
+ }
+
// No sessions saved yet.
synchronized (mQuotaController.mLock) {
mQuotaController.maybeScheduleStartAlarmLocked(
@@ -2150,10 +2317,7 @@ public class QuotaControllerTest {
verify(mAlarmManager, timeout(1000).times(0)).setWindow(
anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
- JobStatus jobStatus = createJobStatus("testMaybeScheduleStartAlarmLocked_Active", 1);
- setStandbyBucket(standbyBucket, jobStatus);
synchronized (mQuotaController.mLock) {
- mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
mQuotaController.prepareForExecutionLocked(jobStatus);
}
advanceElapsedClock(5 * MINUTE_IN_MILLIS);
@@ -2179,19 +2343,24 @@ public class QuotaControllerTest {
// Working set window size is 2 hours.
final int standbyBucket = WORKING_INDEX;
+ JobStatus jobStatus = createJobStatus("testMaybeScheduleStartAlarmLocked_WorkingSet", 1);
+ setStandbyBucket(standbyBucket, jobStatus);
synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
// No sessions saved yet.
- mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ mQuotaController.maybeScheduleStartAlarmLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
}
verify(mAlarmManager, timeout(1000).times(0)).setWindow(
anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
// Test with timing sessions out of window.
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
- mQuotaController.saveTimingSession(0, "com.android.test",
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
createTimingSession(now - 10 * HOUR_IN_MILLIS, 5 * MINUTE_IN_MILLIS, 1), false);
synchronized (mQuotaController.mLock) {
- mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ mQuotaController.maybeScheduleStartAlarmLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
}
verify(mAlarmManager, timeout(1000).times(0)).setWindow(
anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
@@ -2201,37 +2370,41 @@ public class QuotaControllerTest {
// Counting backwards, the quota will come back one minute before the end.
final long expectedAlarmTime =
end - MINUTE_IN_MILLIS + 2 * HOUR_IN_MILLIS + mQcConstants.IN_QUOTA_BUFFER_MS;
- mQuotaController.saveTimingSession(0, "com.android.test",
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
new TimingSession(now - 2 * HOUR_IN_MILLIS, end, 1), false);
synchronized (mQuotaController.mLock) {
- mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ mQuotaController.maybeScheduleStartAlarmLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
}
verify(mAlarmManager, timeout(1000).times(0)).setWindow(
anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
// Add some more sessions, but still in quota.
- mQuotaController.saveTimingSession(0, "com.android.test",
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
createTimingSession(now - HOUR_IN_MILLIS, MINUTE_IN_MILLIS, 1), false);
- mQuotaController.saveTimingSession(0, "com.android.test",
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
createTimingSession(now - (50 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 1), false);
synchronized (mQuotaController.mLock) {
- mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ mQuotaController.maybeScheduleStartAlarmLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
}
verify(mAlarmManager, timeout(1000).times(0)).setWindow(
anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
// Test when out of quota.
- mQuotaController.saveTimingSession(0, "com.android.test",
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
createTimingSession(now - 30 * MINUTE_IN_MILLIS, 5 * MINUTE_IN_MILLIS, 1), false);
synchronized (mQuotaController.mLock) {
- mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ mQuotaController.maybeScheduleStartAlarmLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
}
verify(mAlarmManager, timeout(1000).times(1)).setWindow(
anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
// Alarm already scheduled, so make sure it's not scheduled again.
synchronized (mQuotaController.mLock) {
- mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ mQuotaController.maybeScheduleStartAlarmLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
}
verify(mAlarmManager, timeout(1000).times(1)).setWindow(
anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
@@ -2244,22 +2417,29 @@ public class QuotaControllerTest {
spyOn(mQuotaController);
doNothing().when(mQuotaController).maybeScheduleCleanupAlarmLocked();
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(
+ createJobStatus("testMaybeScheduleStartAlarmLocked_Frequent", 1), null);
+ }
+
// Frequent window size is 8 hours.
final int standbyBucket = FREQUENT_INDEX;
// No sessions saved yet.
synchronized (mQuotaController.mLock) {
- mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ mQuotaController.maybeScheduleStartAlarmLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
}
verify(mAlarmManager, timeout(1000).times(0)).setWindow(
anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
// Test with timing sessions out of window.
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
- mQuotaController.saveTimingSession(0, "com.android.test",
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
createTimingSession(now - 10 * HOUR_IN_MILLIS, 5 * MINUTE_IN_MILLIS, 1), false);
synchronized (mQuotaController.mLock) {
- mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ mQuotaController.maybeScheduleStartAlarmLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
}
verify(mAlarmManager, timeout(1000).times(0)).setWindow(
anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
@@ -2267,37 +2447,41 @@ public class QuotaControllerTest {
// Test with timing sessions in window but still in quota.
final long start = now - (6 * HOUR_IN_MILLIS);
final long expectedAlarmTime = start + 8 * HOUR_IN_MILLIS + mQcConstants.IN_QUOTA_BUFFER_MS;
- mQuotaController.saveTimingSession(0, "com.android.test",
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
createTimingSession(start, 5 * MINUTE_IN_MILLIS, 1), false);
synchronized (mQuotaController.mLock) {
- mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ mQuotaController.maybeScheduleStartAlarmLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
}
verify(mAlarmManager, timeout(1000).times(0)).setWindow(
anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
// Add some more sessions, but still in quota.
- mQuotaController.saveTimingSession(0, "com.android.test",
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
createTimingSession(now - 3 * HOUR_IN_MILLIS, MINUTE_IN_MILLIS, 1), false);
- mQuotaController.saveTimingSession(0, "com.android.test",
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
createTimingSession(now - HOUR_IN_MILLIS, 3 * MINUTE_IN_MILLIS, 1), false);
synchronized (mQuotaController.mLock) {
- mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ mQuotaController.maybeScheduleStartAlarmLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
}
verify(mAlarmManager, timeout(1000).times(0)).setWindow(
anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
// Test when out of quota.
- mQuotaController.saveTimingSession(0, "com.android.test",
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
createTimingSession(now - HOUR_IN_MILLIS, MINUTE_IN_MILLIS, 1), false);
synchronized (mQuotaController.mLock) {
- mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ mQuotaController.maybeScheduleStartAlarmLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
}
verify(mAlarmManager, timeout(1000).times(1)).setWindow(
anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
// Alarm already scheduled, so make sure it's not scheduled again.
synchronized (mQuotaController.mLock) {
- mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ mQuotaController.maybeScheduleStartAlarmLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
}
verify(mAlarmManager, timeout(1000).times(1)).setWindow(
anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
@@ -2314,6 +2498,11 @@ public class QuotaControllerTest {
spyOn(mQuotaController);
doNothing().when(mQuotaController).maybeScheduleCleanupAlarmLocked();
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(
+ createJobStatus("testMaybeScheduleStartAlarmLocked_Never", 1), null);
+ }
+
// The app is really in the NEVER bucket but is elevated somehow (eg via uidActive).
setStandbyBucket(NEVER_INDEX);
final int effectiveStandbyBucket = FREQUENT_INDEX;
@@ -2390,22 +2579,30 @@ public class QuotaControllerTest {
// Rare window size is 24 hours.
final int standbyBucket = RARE_INDEX;
+ JobStatus jobStatus = createJobStatus("testMaybeScheduleStartAlarmLocked_Rare", 1);
+ setStandbyBucket(standbyBucket, jobStatus);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
+ }
+
// Prevent timing session throttling from affecting the test.
setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_RARE, 50);
// No sessions saved yet.
synchronized (mQuotaController.mLock) {
- mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ mQuotaController.maybeScheduleStartAlarmLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
}
verify(mAlarmManager, timeout(1000).times(0)).setWindow(
anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
// Test with timing sessions out of window.
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
- mQuotaController.saveTimingSession(0, "com.android.test",
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
createTimingSession(now - 25 * HOUR_IN_MILLIS, 5 * MINUTE_IN_MILLIS, 1), false);
synchronized (mQuotaController.mLock) {
- mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ mQuotaController.maybeScheduleStartAlarmLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
}
verify(mAlarmManager, timeout(1000).times(0)).setWindow(
anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
@@ -2417,42 +2614,168 @@ public class QuotaControllerTest {
final long expectedAlarmTime =
start + MINUTE_IN_MILLIS + 24 * HOUR_IN_MILLIS
+ mQcConstants.IN_QUOTA_BUFFER_MS;
- mQuotaController.saveTimingSession(0, "com.android.test",
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
createTimingSession(start, 5 * MINUTE_IN_MILLIS, 1), false);
synchronized (mQuotaController.mLock) {
- mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ mQuotaController.maybeScheduleStartAlarmLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
}
verify(mAlarmManager, timeout(1000).times(0)).setWindow(
anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
// Add some more sessions, but still in quota.
- mQuotaController.saveTimingSession(0, "com.android.test",
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
createTimingSession(now - 3 * HOUR_IN_MILLIS, MINUTE_IN_MILLIS, 1), false);
- mQuotaController.saveTimingSession(0, "com.android.test",
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
createTimingSession(now - HOUR_IN_MILLIS, 3 * MINUTE_IN_MILLIS, 1), false);
synchronized (mQuotaController.mLock) {
- mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ mQuotaController.maybeScheduleStartAlarmLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
}
verify(mAlarmManager, timeout(1000).times(0)).setWindow(
anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
// Test when out of quota.
- mQuotaController.saveTimingSession(0, "com.android.test",
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
createTimingSession(now - HOUR_IN_MILLIS, 2 * MINUTE_IN_MILLIS, 1), false);
synchronized (mQuotaController.mLock) {
- mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ mQuotaController.maybeScheduleStartAlarmLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
}
verify(mAlarmManager, timeout(1000).times(1)).setWindow(
anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
// Alarm already scheduled, so make sure it's not scheduled again.
synchronized (mQuotaController.mLock) {
- mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
+ mQuotaController.maybeScheduleStartAlarmLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
}
verify(mAlarmManager, timeout(1000).times(1)).setWindow(
anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
}
+ @Test
+ public void testMaybeScheduleStartAlarmLocked_Priority() {
+ // saveTimingSession calls maybeScheduleCleanupAlarmLocked which interferes with these tests
+ // because it schedules an alarm too. Prevent it from doing so.
+ spyOn(mQuotaController);
+ doNothing().when(mQuotaController).maybeScheduleCleanupAlarmLocked();
+
+ setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_RARE, 5);
+
+ final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - (24 * HOUR_IN_MILLIS), MINUTE_IN_MILLIS, 1), false);
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - (7 * HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 1), false);
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - (HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 1), false);
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - (5 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 1), false);
+
+ InOrder inOrder = inOrder(mAlarmManager);
+
+ JobStatus jobDef = createJobStatus("testMaybeScheduleStartAlarmLocked_Priority",
+ SOURCE_PACKAGE, CALLING_UID,
+ new JobInfo.Builder(1, new ComponentName(mContext, "TestQuotaJobService"))
+ .setPriority(PRIORITY_DEFAULT)
+ .build());
+ JobStatus jobLow = createJobStatus("testMaybeScheduleStartAlarmLocked_Priority",
+ SOURCE_PACKAGE, CALLING_UID,
+ new JobInfo.Builder(2, new ComponentName(mContext, "TestQuotaJobService"))
+ .setPriority(PRIORITY_LOW)
+ .build());
+ JobStatus jobMin = createJobStatus("testMaybeScheduleStartAlarmLocked_Priority",
+ SOURCE_PACKAGE, CALLING_UID,
+ new JobInfo.Builder(3, new ComponentName(mContext, "TestQuotaJobService"))
+ .setPriority(PRIORITY_MIN)
+ .build());
+
+ setStandbyBucket(RARE_INDEX, jobDef, jobLow, jobMin);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(jobMin, null);
+ mQuotaController.maybeScheduleStartAlarmLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, RARE_INDEX);
+ // Min job requires 5 mins of surplus.
+ long expectedAlarmTime = now + 23 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS;
+ inOrder.verify(mAlarmManager, timeout(1000).times(1)).setWindow(
+ anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+
+ mQuotaController.maybeStartTrackingJobLocked(jobLow, null);
+ mQuotaController.maybeScheduleStartAlarmLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, RARE_INDEX);
+ // Low job requires 2.5 mins of surplus.
+ expectedAlarmTime = now + 17 * HOUR_IN_MILLIS + 90 * SECOND_IN_MILLIS;
+ inOrder.verify(mAlarmManager, timeout(1000).times(1)).setWindow(
+ anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+
+ mQuotaController.maybeStartTrackingJobLocked(jobDef, null);
+ mQuotaController.maybeScheduleStartAlarmLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, RARE_INDEX);
+ // Default+ jobs require IN_QUOTA_BUFFER_MS.
+ expectedAlarmTime = now + mQcConstants.IN_QUOTA_BUFFER_MS;
+ inOrder.verify(mAlarmManager, timeout(1000).times(1)).setWindow(
+ anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+
+ mQuotaController.maybeStopTrackingJobLocked(jobMin, null, false);
+ mQuotaController.maybeStopTrackingJobLocked(jobLow, null, false);
+ mQuotaController.maybeStopTrackingJobLocked(jobDef, null, false);
+
+ setStandbyBucket(FREQUENT_INDEX, jobDef, jobLow, jobMin);
+
+ mQuotaController.maybeStartTrackingJobLocked(jobMin, null);
+ mQuotaController.maybeScheduleStartAlarmLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, FREQUENT_INDEX);
+ // Min job requires 5 mins of surplus.
+ expectedAlarmTime = now + 7 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS;
+ inOrder.verify(mAlarmManager, timeout(1000).times(1)).setWindow(
+ anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+
+ mQuotaController.maybeStartTrackingJobLocked(jobLow, null);
+ mQuotaController.maybeScheduleStartAlarmLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, FREQUENT_INDEX);
+ // Low job requires 2.5 mins of surplus.
+ expectedAlarmTime = now + HOUR_IN_MILLIS + 90 * SECOND_IN_MILLIS;
+ inOrder.verify(mAlarmManager, timeout(1000).times(1)).setWindow(
+ anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+
+ mQuotaController.maybeStartTrackingJobLocked(jobDef, null);
+ mQuotaController.maybeScheduleStartAlarmLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, FREQUENT_INDEX);
+ // Default+ jobs already have enough quota.
+ inOrder.verify(mAlarmManager, timeout(1000).times(0)).setWindow(
+ anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+
+ mQuotaController.maybeStopTrackingJobLocked(jobMin, null, false);
+ mQuotaController.maybeStopTrackingJobLocked(jobLow, null, false);
+ mQuotaController.maybeStopTrackingJobLocked(jobDef, null, false);
+
+ setStandbyBucket(WORKING_INDEX, jobDef, jobLow, jobMin);
+
+ mQuotaController.maybeStartTrackingJobLocked(jobMin, null);
+ mQuotaController.maybeScheduleStartAlarmLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX);
+ // Min job requires 5 mins of surplus.
+ expectedAlarmTime = now + HOUR_IN_MILLIS + MINUTE_IN_MILLIS;
+ inOrder.verify(mAlarmManager, timeout(1000).times(1)).setWindow(
+ anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+
+ mQuotaController.maybeStartTrackingJobLocked(jobLow, null);
+ mQuotaController.maybeScheduleStartAlarmLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX);
+ // Low job has enough surplus.
+ inOrder.verify(mAlarmManager, timeout(1000).times(0)).setWindow(
+ anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+
+ mQuotaController.maybeStartTrackingJobLocked(jobDef, null);
+ mQuotaController.maybeScheduleStartAlarmLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX);
+ // Default+ jobs already have enough quota.
+ inOrder.verify(mAlarmManager, timeout(1000).times(0)).setWindow(
+ anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+ }
+ }
+
/** Tests that the start alarm is properly rescheduled if the app's bucket is changed. */
@Test
public void testMaybeScheduleStartAlarmLocked_BucketChange() {
@@ -2464,24 +2787,29 @@ public class QuotaControllerTest {
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
// Affects rare bucket
- mQuotaController.saveTimingSession(0, "com.android.test",
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
createTimingSession(now - 12 * HOUR_IN_MILLIS, 9 * MINUTE_IN_MILLIS, 3), false);
// Affects frequent and rare buckets
- mQuotaController.saveTimingSession(0, "com.android.test",
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
createTimingSession(now - 4 * HOUR_IN_MILLIS, 4 * MINUTE_IN_MILLIS, 3), false);
// Affects working, frequent, and rare buckets
final long outOfQuotaTime = now - HOUR_IN_MILLIS;
- mQuotaController.saveTimingSession(0, "com.android.test",
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
createTimingSession(outOfQuotaTime, 7 * MINUTE_IN_MILLIS, 10), false);
// Affects all buckets
- mQuotaController.saveTimingSession(0, "com.android.test",
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
createTimingSession(now - 5 * MINUTE_IN_MILLIS, 3 * MINUTE_IN_MILLIS, 3), false);
InOrder inOrder = inOrder(mAlarmManager);
+ JobStatus jobStatus = createJobStatus("testMaybeScheduleStartAlarmLocked_BucketChange", 1);
+
// Start in ACTIVE bucket.
+ setStandbyBucket(ACTIVE_INDEX, jobStatus);
synchronized (mQuotaController.mLock) {
- mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", ACTIVE_INDEX);
+ mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
+ mQuotaController.maybeScheduleStartAlarmLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, ACTIVE_INDEX);
}
inOrder.verify(mAlarmManager, timeout(1000).times(0))
.setWindow(anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
@@ -2492,8 +2820,10 @@ public class QuotaControllerTest {
final long expectedWorkingAlarmTime =
outOfQuotaTime + (2 * HOUR_IN_MILLIS)
+ mQcConstants.IN_QUOTA_BUFFER_MS;
+ setStandbyBucket(WORKING_INDEX, jobStatus);
synchronized (mQuotaController.mLock) {
- mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", WORKING_INDEX);
+ mQuotaController.maybeScheduleStartAlarmLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX);
}
inOrder.verify(mAlarmManager, timeout(1000).times(1)).setWindow(
anyInt(), eq(expectedWorkingAlarmTime), anyLong(),
@@ -2502,8 +2832,10 @@ public class QuotaControllerTest {
final long expectedFrequentAlarmTime =
outOfQuotaTime + (8 * HOUR_IN_MILLIS)
+ mQcConstants.IN_QUOTA_BUFFER_MS;
+ setStandbyBucket(FREQUENT_INDEX, jobStatus);
synchronized (mQuotaController.mLock) {
- mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", FREQUENT_INDEX);
+ mQuotaController.maybeScheduleStartAlarmLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, FREQUENT_INDEX);
}
inOrder.verify(mAlarmManager, timeout(1000).times(1)).setWindow(
anyInt(), eq(expectedFrequentAlarmTime), anyLong(),
@@ -2512,29 +2844,37 @@ public class QuotaControllerTest {
final long expectedRareAlarmTime =
outOfQuotaTime + (24 * HOUR_IN_MILLIS)
+ mQcConstants.IN_QUOTA_BUFFER_MS;
+ setStandbyBucket(RARE_INDEX, jobStatus);
synchronized (mQuotaController.mLock) {
- mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", RARE_INDEX);
+ mQuotaController.maybeScheduleStartAlarmLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, RARE_INDEX);
}
inOrder.verify(mAlarmManager, timeout(1000).times(1)).setWindow(
anyInt(), eq(expectedRareAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
// And back up again.
+ setStandbyBucket(FREQUENT_INDEX, jobStatus);
synchronized (mQuotaController.mLock) {
- mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", FREQUENT_INDEX);
+ mQuotaController.maybeScheduleStartAlarmLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, FREQUENT_INDEX);
}
inOrder.verify(mAlarmManager, timeout(1000).times(1)).setWindow(
anyInt(), eq(expectedFrequentAlarmTime), anyLong(),
eq(TAG_QUOTA_CHECK), any(), any());
+ setStandbyBucket(WORKING_INDEX, jobStatus);
synchronized (mQuotaController.mLock) {
- mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", WORKING_INDEX);
+ mQuotaController.maybeScheduleStartAlarmLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX);
}
inOrder.verify(mAlarmManager, timeout(1000).times(1)).setWindow(
anyInt(), eq(expectedWorkingAlarmTime), anyLong(),
eq(TAG_QUOTA_CHECK), any(), any());
+ setStandbyBucket(ACTIVE_INDEX, jobStatus);
synchronized (mQuotaController.mLock) {
- mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", ACTIVE_INDEX);
+ mQuotaController.maybeScheduleStartAlarmLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, ACTIVE_INDEX);
}
inOrder.verify(mAlarmManager, timeout(1000).times(0))
.setWindow(anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
@@ -2551,6 +2891,13 @@ public class QuotaControllerTest {
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
final int standbyBucket = WORKING_INDEX;
+
+ JobStatus jobStatus = createJobStatus("testMaybeScheduleStartAlarmLocked", 1);
+ setStandbyBucket(standbyBucket, jobStatus);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
+ }
+
ExecutionStats stats;
synchronized (mQuotaController.mLock) {
stats = mQuotaController.getExecutionStatsLocked(
@@ -2646,6 +2993,11 @@ public class QuotaControllerTest {
spyOn(mQuotaController);
doNothing().when(mQuotaController).maybeScheduleCleanupAlarmLocked();
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(
+ createJobStatus("testMaybeScheduleStartAlarmLocked", 1), null);
+ }
+
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
// Working set window size is 2 hours.
final int standbyBucket = WORKING_INDEX;
@@ -2671,13 +3023,17 @@ public class QuotaControllerTest {
anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
}
-
private void runTestMaybeScheduleStartAlarmLocked_SmallRollingQuota_MaxTimeCheck() {
// saveTimingSession calls maybeScheduleCleanupAlarmLocked which interferes with these tests
// because it schedules an alarm too. Prevent it from doing so.
spyOn(mQuotaController);
doNothing().when(mQuotaController).maybeScheduleCleanupAlarmLocked();
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(
+ createJobStatus("testMaybeScheduleStartAlarmLocked", 1), null);
+ }
+
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
// Working set window size is 2 hours.
final int standbyBucket = WORKING_INDEX;
@@ -2708,6 +3064,8 @@ public class QuotaControllerTest {
public void testConstantsUpdating_ValidValues() {
setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_MS, 5 * MINUTE_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_IN_QUOTA_BUFFER_MS, 2 * MINUTE_IN_MILLIS);
+ setDeviceConfigFloat(QcConstants.KEY_ALLOWED_TIME_SURPLUS_PRIORITY_LOW, .7f);
+ setDeviceConfigFloat(QcConstants.KEY_ALLOWED_TIME_SURPLUS_PRIORITY_MIN, .2f);
setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_ACTIVE_MS, 15 * MINUTE_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_WORKING_MS, 30 * MINUTE_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_FREQUENT_MS, 45 * MINUTE_IN_MILLIS);
@@ -2748,6 +3106,8 @@ public class QuotaControllerTest {
assertEquals(5 * MINUTE_IN_MILLIS, mQuotaController.getAllowedTimePerPeriodMs());
assertEquals(2 * MINUTE_IN_MILLIS, mQuotaController.getInQuotaBufferMs());
+ assertEquals(.7f, mQuotaController.getAllowedTimeSurplusPriorityLow(), 1e-6);
+ assertEquals(.2f, mQuotaController.getAllowedTimeSurplusPriorityMin(), 1e-6);
assertEquals(15 * MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[ACTIVE_INDEX]);
assertEquals(30 * MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[WORKING_INDEX]);
assertEquals(45 * MINUTE_IN_MILLIS,
@@ -2793,6 +3153,8 @@ public class QuotaControllerTest {
// Test negatives/too low.
setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_MS, -MINUTE_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_IN_QUOTA_BUFFER_MS, -MINUTE_IN_MILLIS);
+ setDeviceConfigFloat(QcConstants.KEY_ALLOWED_TIME_SURPLUS_PRIORITY_LOW, -.1f);
+ setDeviceConfigFloat(QcConstants.KEY_ALLOWED_TIME_SURPLUS_PRIORITY_MIN, -.01f);
setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_ACTIVE_MS, -MINUTE_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_WORKING_MS, -MINUTE_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_FREQUENT_MS, -MINUTE_IN_MILLIS);
@@ -2831,6 +3193,8 @@ public class QuotaControllerTest {
assertEquals(MINUTE_IN_MILLIS, mQuotaController.getAllowedTimePerPeriodMs());
assertEquals(0, mQuotaController.getInQuotaBufferMs());
+ assertEquals(0f, mQuotaController.getAllowedTimeSurplusPriorityLow(), 1e-6);
+ assertEquals(0f, mQuotaController.getAllowedTimeSurplusPriorityMin(), 1e-6);
assertEquals(MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[ACTIVE_INDEX]);
assertEquals(MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[WORKING_INDEX]);
assertEquals(MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[FREQUENT_INDEX]);
@@ -2878,6 +3242,8 @@ public class QuotaControllerTest {
// Test larger than a day. Controller should cap at one day.
setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_MS, 25 * HOUR_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_IN_QUOTA_BUFFER_MS, 25 * HOUR_IN_MILLIS);
+ setDeviceConfigFloat(QcConstants.KEY_ALLOWED_TIME_SURPLUS_PRIORITY_LOW, 1f);
+ setDeviceConfigFloat(QcConstants.KEY_ALLOWED_TIME_SURPLUS_PRIORITY_MIN, .95f);
setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_ACTIVE_MS, 25 * HOUR_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_WORKING_MS, 25 * HOUR_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_FREQUENT_MS, 25 * HOUR_IN_MILLIS);
@@ -2905,6 +3271,8 @@ public class QuotaControllerTest {
assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getAllowedTimePerPeriodMs());
assertEquals(5 * MINUTE_IN_MILLIS, mQuotaController.getInQuotaBufferMs());
+ assertEquals(.9f, mQuotaController.getAllowedTimeSurplusPriorityLow(), 1e-6);
+ assertEquals(.9f, mQuotaController.getAllowedTimeSurplusPriorityMin(), 1e-6);
assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getBucketWindowSizes()[ACTIVE_INDEX]);
assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getBucketWindowSizes()[WORKING_INDEX]);
assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getBucketWindowSizes()[FREQUENT_INDEX]);
@@ -3669,8 +4037,8 @@ public class QuotaControllerTest {
// Wait for some extra time to allow for job processing.
inOrder.verify(mJobSchedulerService,
- timeout(remainingTimeMs + 2 * SECOND_IN_MILLIS).times(0))
- .onControllerStateChanged(any());
+ timeout(remainingTimeMs + 2 * SECOND_IN_MILLIS).times(0))
+ .onControllerStateChanged(argThat(jobs -> jobs.size() > 0));
synchronized (mQuotaController.mLock) {
assertEquals(remainingTimeMs / 2,
mQuotaController.getRemainingExecutionTimeLocked(jobBg));
@@ -3681,8 +4049,8 @@ public class QuotaControllerTest {
setProcessState(ActivityManager.PROCESS_STATE_TOP_SLEEPING);
advanceElapsedClock(remainingTimeMs / 2 + 1);
inOrder.verify(mJobSchedulerService,
- timeout(remainingTimeMs / 2 + 2 * SECOND_IN_MILLIS).times(1))
- .onControllerStateChanged(any());
+ timeout(remainingTimeMs / 2 + 2 * SECOND_IN_MILLIS).times(1))
+ .onControllerStateChanged(argThat(jobs -> jobs.size() == 1));
// Top job should still be allowed to run.
assertFalse(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
assertTrue(jobTop.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
@@ -3696,7 +4064,7 @@ public class QuotaControllerTest {
advanceElapsedClock(20 * SECOND_IN_MILLIS);
setProcessState(ActivityManager.PROCESS_STATE_TOP);
inOrder.verify(mJobSchedulerService, timeout(SECOND_IN_MILLIS).times(1))
- .onControllerStateChanged(any());
+ .onControllerStateChanged(argThat(jobs -> jobs.size() == 1));
trackJobs(jobFg, jobTop);
synchronized (mQuotaController.mLock) {
mQuotaController.prepareForExecutionLocked(jobTop);
@@ -3715,7 +4083,7 @@ public class QuotaControllerTest {
advanceElapsedClock(20 * SECOND_IN_MILLIS);
setProcessState(ActivityManager.PROCESS_STATE_SERVICE);
inOrder.verify(mJobSchedulerService, timeout(SECOND_IN_MILLIS).times(1))
- .onControllerStateChanged(any());
+ .onControllerStateChanged(argThat(jobs -> jobs.size() == 2));
// App is now in background and out of quota. Fg should now change to out of quota since it
// wasn't started. Top should remain in quota since it started when the app was in TOP.
assertTrue(jobTop.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
@@ -3753,7 +4121,7 @@ public class QuotaControllerTest {
// Wait for some extra time to allow for job processing.
verify(mJobSchedulerService,
timeout(remainingTimeMs + 2 * SECOND_IN_MILLIS).times(1))
- .onControllerStateChanged(any());
+ .onControllerStateChanged(argThat(jobs -> jobs.size() == 1));
assertFalse(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
assertEquals(JobSchedulerService.sElapsedRealtimeClock.millis(),
jobStatus.getWhenStandbyDeferred());
@@ -3797,7 +4165,7 @@ public class QuotaControllerTest {
// Wait for some extra time to allow for job processing.
verify(mJobSchedulerService,
timeout(remainingTimeMs + 2 * SECOND_IN_MILLIS).times(0))
- .onControllerStateChanged(any());
+ .onControllerStateChanged(argThat(jobs -> jobs.size() > 0));
assertTrue(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
// The job used up the remaining quota, but in that time, the same amount of time in the
// old TimingSession also fell out of the quota window, so it should still have the same
@@ -3819,7 +4187,7 @@ public class QuotaControllerTest {
// Wait for some extra time to allow for job processing.
verify(mJobSchedulerService,
timeout(12 * SECOND_IN_MILLIS).times(1))
- .onControllerStateChanged(any());
+ .onControllerStateChanged(argThat(jobs -> jobs.size() == 1));
assertFalse(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
verify(handler, never()).sendMessageDelayed(any(), anyInt());
}
@@ -4406,6 +4774,11 @@ public class QuotaControllerTest {
spyOn(mQuotaController);
doNothing().when(mQuotaController).maybeScheduleCleanupAlarmLocked();
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(
+ createJobStatus("testMaybeScheduleStartAlarmLocked_EJ", 1), null);
+ }
+
final int standbyBucket = WORKING_INDEX;
setStandbyBucket(standbyBucket);
setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_WORKING_MS, 20 * MINUTE_IN_MILLIS);
@@ -4482,6 +4855,11 @@ public class QuotaControllerTest {
spyOn(mQuotaController);
doNothing().when(mQuotaController).maybeScheduleCleanupAlarmLocked();
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(
+ createJobStatus("testMaybeScheduleStartAlarmLocked_Ej_BucketChange", 1), null);
+ }
+
setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_ACTIVE_MS, 30 * MINUTE_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_WORKING_MS, 20 * MINUTE_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_FREQUENT_MS, 15 * MINUTE_IN_MILLIS);
@@ -4590,6 +4968,11 @@ public class QuotaControllerTest {
spyOn(mQuotaController);
doNothing().when(mQuotaController).maybeScheduleCleanupAlarmLocked();
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(
+ createJobStatus("testMaybeScheduleStartAlarmLocked_Ej_SRQ", 1), null);
+ }
+
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
setStandbyBucket(WORKING_INDEX);
final long contributionMs = mQcConstants.IN_QUOTA_BUFFER_MS / 2;
@@ -5437,8 +5820,8 @@ public class QuotaControllerTest {
// Wait for some extra time to allow for job processing.
inOrder.verify(mJobSchedulerService,
- timeout(remainingTimeMs + 2 * SECOND_IN_MILLIS).times(0))
- .onControllerStateChanged(any());
+ timeout(remainingTimeMs + 2 * SECOND_IN_MILLIS).times(0))
+ .onControllerStateChanged(argThat(jobs -> jobs.size() > 0));
synchronized (mQuotaController.mLock) {
assertEquals(remainingTimeMs / 2,
mQuotaController.getRemainingEJExecutionTimeLocked(
@@ -5448,8 +5831,8 @@ public class QuotaControllerTest {
setProcessState(ActivityManager.PROCESS_STATE_TOP_SLEEPING);
advanceElapsedClock(remainingTimeMs / 2 + 1);
inOrder.verify(mJobSchedulerService,
- timeout(remainingTimeMs / 2 + 2 * SECOND_IN_MILLIS).times(1))
- .onControllerStateChanged(any());
+ timeout(remainingTimeMs / 2 + 2 * SECOND_IN_MILLIS).times(1))
+ .onControllerStateChanged(argThat(jobs -> jobs.size() == 2));
// Top should still be "in quota" since it started before the app ran on top out of quota.
assertFalse(jobBg.isExpeditedQuotaApproved());
assertTrue(jobTop.isExpeditedQuotaApproved());
@@ -5473,7 +5856,7 @@ public class QuotaControllerTest {
setProcessState(ActivityManager.PROCESS_STATE_TOP);
// Confirm QC recognizes that jobUnstarted has changed from out-of-quota to in-quota.
inOrder.verify(mJobSchedulerService, timeout(SECOND_IN_MILLIS).times(1))
- .onControllerStateChanged(any());
+ .onControllerStateChanged(argThat(jobs -> jobs.size() == 2));
trackJobs(jobTop2, jobFg);
synchronized (mQuotaController.mLock) {
mQuotaController.prepareForExecutionLocked(jobTop2);
@@ -5494,7 +5877,7 @@ public class QuotaControllerTest {
advanceElapsedClock(20 * SECOND_IN_MILLIS);
setProcessState(ActivityManager.PROCESS_STATE_SERVICE);
inOrder.verify(mJobSchedulerService, timeout(SECOND_IN_MILLIS).times(1))
- .onControllerStateChanged(any());
+ .onControllerStateChanged(argThat(jobs -> jobs.size() == 3));
// App is now in background and out of quota. Fg should now change to out of quota since it
// wasn't started. Top should remain in quota since it started when the app was in TOP.
assertTrue(jobTop2.isExpeditedQuotaApproved());
@@ -5633,7 +6016,7 @@ public class QuotaControllerTest {
// Wait for some extra time to allow for job processing.
verify(mJobSchedulerService,
timeout(remainingTimeMs + 2 * SECOND_IN_MILLIS).times(0))
- .onControllerStateChanged(any());
+ .onControllerStateChanged(argThat(jobs -> jobs.size() > 0));
assertTrue(jobStatus.isExpeditedQuotaApproved());
// The job used up the remaining quota, but in that time, the same amount of time in the
// old TimingSession also fell out of the quota window, so it should still have the same
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/gnss/hal/FakeGnssHal.java b/services/tests/mockingservicestests/src/com/android/server/location/gnss/hal/FakeGnssHal.java
index 93a2d317c40e..b7ab6f80167e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/gnss/hal/FakeGnssHal.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/gnss/hal/FakeGnssHal.java
@@ -109,15 +109,19 @@ public final class FakeGnssHal extends GnssNative.GnssHal {
public static class GnssHalBatchingMode {
public final long PeriodNanos;
+ public final float MinUpdateDistanceMeters;
public final boolean WakeOnFifoFull;
GnssHalBatchingMode() {
PeriodNanos = 0;
+ MinUpdateDistanceMeters = 0.0f;
WakeOnFifoFull = false;
}
- public GnssHalBatchingMode(long periodNanos, boolean wakeOnFifoFull) {
+ public GnssHalBatchingMode(long periodNanos, float minUpdateDistanceMeters,
+ boolean wakeOnFifoFull) {
PeriodNanos = periodNanos;
+ MinUpdateDistanceMeters = minUpdateDistanceMeters;
WakeOnFifoFull = wakeOnFifoFull;
}
@@ -132,12 +136,13 @@ public final class FakeGnssHal extends GnssNative.GnssHal {
GnssHalBatchingMode that = (GnssHalBatchingMode) o;
return PeriodNanos == that.PeriodNanos
+ && MinUpdateDistanceMeters == that.MinUpdateDistanceMeters
&& WakeOnFifoFull == that.WakeOnFifoFull;
}
@Override
public int hashCode() {
- return Objects.hash(PeriodNanos, WakeOnFifoFull);
+ return Objects.hash(PeriodNanos, MinUpdateDistanceMeters, WakeOnFifoFull);
}
}
@@ -570,9 +575,11 @@ public final class FakeGnssHal extends GnssNative.GnssHal {
protected void cleanupBatching() {}
@Override
- protected boolean startBatch(long periodNanos, boolean wakeOnFifoFull) {
+ protected boolean startBatch(long periodNanos, float minUpdateDistanceMeters,
+ boolean wakeOnFifoFull) {
mState.mBatchingStarted = true;
- mState.mBatchingMode = new GnssHalBatchingMode(periodNanos, wakeOnFifoFull);
+ mState.mBatchingMode = new GnssHalBatchingMode(periodNanos, minUpdateDistanceMeters,
+ wakeOnFifoFull);
return true;
}
@@ -673,7 +680,8 @@ public final class FakeGnssHal extends GnssNative.GnssHal {
protected void setAgpsSetId(int type, String setId) {}
@Override
- protected void setAgpsReferenceLocationCellId(int type, int mcc, int mnc, int lac, int cid) {}
+ protected void setAgpsReferenceLocationCellId(int type, int mcc, int mnc, int lac, long cid,
+ int tac, int pcid, int arfcn) {}
@Override
protected boolean isPsdsSupported() {
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 c2e0a04e3caa..555f4b8b5cac 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
@@ -216,6 +216,7 @@ class MockSystem(withSession: (StaticMockitoSessionBuilder) -> Unit = {}) {
val displayMetrics: DisplayMetrics = mock()
val domainVerificationManagerInternal: DomainVerificationManagerInternal = mock()
val handler = TestHandler(null)
+ val defaultAppProvider: DefaultAppProvider = mock()
}
companion object {
@@ -294,6 +295,7 @@ class MockSystem(withSession: (StaticMockitoSessionBuilder) -> Unit = {}) {
whenever(mocks.injector.domainVerificationManagerInternal)
.thenReturn(mocks.domainVerificationManagerInternal)
whenever(mocks.injector.handler) { mocks.handler }
+ whenever(mocks.injector.defaultAppProvider) { mocks.defaultAppProvider }
wheneverStatic { SystemConfig.getInstance() }.thenReturn(mocks.systemConfig)
whenever(mocks.systemConfig.availableFeatures).thenReturn(DEFAULT_AVAILABLE_FEATURES_MAP)
whenever(mocks.systemConfig.sharedLibraries).thenReturn(DEFAULT_SHARED_LIBRARIES_LIST)
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt
new file mode 100644
index 000000000000..fe7e2d9eb047
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt
@@ -0,0 +1,507 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm
+
+import android.content.Intent
+import android.content.pm.PackageManagerInternal
+import android.content.pm.SuspendDialogInfo
+import android.os.Binder
+import android.os.Build
+import android.os.Bundle
+import android.os.PersistableBundle
+import android.os.UserHandle
+import android.os.UserManager
+import android.util.ArrayMap
+import android.util.SparseArray
+import com.android.server.pm.pkg.PackageStateInternal
+import com.android.server.testutils.TestHandler
+import com.android.server.testutils.any
+import com.android.server.testutils.eq
+import com.android.server.testutils.nullable
+import com.android.server.testutils.whenever
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.argThat
+import org.mockito.Mockito.spy
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@RunWith(JUnit4::class)
+class SuspendPackageHelperTest {
+
+ companion object {
+ const val TEST_PACKAGE_1 = "com.android.test.package1"
+ const val TEST_PACKAGE_2 = "com.android.test.package2"
+ const val DEVICE_OWNER_PACKAGE = "com.android.test.owner"
+ const val NONEXISTENT_PACKAGE = "com.android.test.nonexistent"
+ const val DEVICE_ADMIN_PACKAGE = "com.android.test.known.device.admin"
+ const val DEFAULT_HOME_PACKAGE = "com.android.test.known.home"
+ const val DIALER_PACKAGE = "com.android.test.known.dialer"
+ const val INSTALLER_PACKAGE = "com.android.test.known.installer"
+ const val UNINSTALLER_PACKAGE = "com.android.test.known.uninstaller"
+ const val VERIFIER_PACKAGE = "com.android.test.known.verifier"
+ const val PERMISSION_CONTROLLER_PACKAGE = "com.android.test.known.permission"
+ const val TEST_USER_ID = 0
+ }
+
+ lateinit var pms: PackageManagerService
+ lateinit var suspendPackageHelper: SuspendPackageHelper
+ lateinit var testHandler: TestHandler
+ lateinit var defaultAppProvider: DefaultAppProvider
+ lateinit var packageSetting1: PackageStateInternal
+ lateinit var packageSetting2: PackageStateInternal
+ lateinit var ownerSetting: PackageStateInternal
+ lateinit var packagesToSuspend: Array<String>
+ lateinit var uidsToSuspend: IntArray
+
+ @Mock
+ lateinit var broadcastHelper: BroadcastHelper
+ @Mock
+ lateinit var protectedPackages: ProtectedPackages
+
+ @Captor
+ lateinit var bundleCaptor: ArgumentCaptor<Bundle>
+
+ @Rule
+ @JvmField
+ val rule = MockSystemRule()
+ var deviceOwnerUid = 0
+
+ @Before
+ @Throws(Exception::class)
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ rule.system().stageNominalSystemState()
+ pms = spy(createPackageManagerService(
+ TEST_PACKAGE_1, TEST_PACKAGE_2, DEVICE_OWNER_PACKAGE, DEVICE_ADMIN_PACKAGE,
+ DEFAULT_HOME_PACKAGE, DIALER_PACKAGE, INSTALLER_PACKAGE, UNINSTALLER_PACKAGE,
+ VERIFIER_PACKAGE, PERMISSION_CONTROLLER_PACKAGE))
+ suspendPackageHelper = SuspendPackageHelper(
+ pms, rule.mocks().injector, broadcastHelper, protectedPackages)
+ defaultAppProvider = rule.mocks().defaultAppProvider
+ testHandler = rule.mocks().handler
+ packageSetting1 = pms.getPackageStateInternal(TEST_PACKAGE_1)!!
+ packageSetting2 = pms.getPackageStateInternal(TEST_PACKAGE_2)!!
+ ownerSetting = pms.getPackageStateInternal(DEVICE_OWNER_PACKAGE)!!
+ deviceOwnerUid = UserHandle.getUid(TEST_USER_ID, ownerSetting.appId)
+ packagesToSuspend = arrayOf(TEST_PACKAGE_1, TEST_PACKAGE_2)
+ uidsToSuspend = intArrayOf(packageSetting1.appId, packageSetting2.appId)
+
+ whenever(protectedPackages.getDeviceOwnerOrProfileOwnerPackage(eq(TEST_USER_ID)))
+ .thenReturn(DEVICE_OWNER_PACKAGE)
+ whenever(rule.mocks().userManagerService.hasUserRestriction(
+ eq(UserManager.DISALLOW_APPS_CONTROL), eq(TEST_USER_ID))).thenReturn(true)
+ whenever(rule.mocks().userManagerService.hasUserRestriction(
+ eq(UserManager.DISALLOW_UNINSTALL_APPS), eq(TEST_USER_ID))).thenReturn(true)
+ mockKnownPackages(pms)
+ }
+
+ @Test
+ fun setPackagesSuspended() {
+ val targetPackages = arrayOf(TEST_PACKAGE_1, TEST_PACKAGE_2)
+ val failedNames = suspendPackageHelper.setPackagesSuspended(targetPackages,
+ true /* suspended */, null /* appExtras */, null /* launcherExtras */,
+ null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid)
+ testHandler.flush()
+
+ verify(pms).scheduleWritePackageRestrictionsLocked(eq(TEST_USER_ID))
+ verify(broadcastHelper).sendPackageBroadcast(eq(Intent.ACTION_PACKAGES_SUSPENDED),
+ nullable(), bundleCaptor.capture(), anyInt(), nullable(), nullable(), any(),
+ nullable(), nullable(), nullable())
+ verify(broadcastHelper).doSendBroadcast(eq(Intent.ACTION_MY_PACKAGE_SUSPENDED), nullable(),
+ nullable(), any(), eq(TEST_PACKAGE_1), nullable(), any(), any(), nullable(), nullable())
+ verify(broadcastHelper).doSendBroadcast(eq(Intent.ACTION_MY_PACKAGE_SUSPENDED), nullable(),
+ nullable(), any(), eq(TEST_PACKAGE_2), nullable(), any(), any(), nullable(), nullable())
+
+ var modifiedPackages = bundleCaptor.value.getStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST)
+ assertThat(modifiedPackages).asList().containsExactly(TEST_PACKAGE_1, TEST_PACKAGE_2)
+ assertThat(failedNames).isEmpty()
+ }
+
+ @Test
+ fun setPackagesSuspended_emptyPackageName() {
+ var failedNames = suspendPackageHelper.setPackagesSuspended(null /* packageNames */,
+ true /* suspended */, null /* appExtras */, null /* launcherExtras */,
+ null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid)
+
+ assertThat(failedNames).isNull()
+
+ failedNames = suspendPackageHelper.setPackagesSuspended(arrayOfNulls(0),
+ true /* suspended */, null /* appExtras */, null /* launcherExtras */,
+ null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid)
+
+ assertThat(failedNames).isEmpty()
+ }
+
+ @Test
+ fun setPackagesSuspended_callerIsNotAllowed() {
+ val failedNames = suspendPackageHelper.setPackagesSuspended(arrayOf(TEST_PACKAGE_2),
+ true /* suspended */, null /* appExtras */, null /* launcherExtras */,
+ null /* dialogInfo */, TEST_PACKAGE_1, TEST_USER_ID, Binder.getCallingUid())
+
+ assertThat(failedNames).asList().hasSize(1)
+ assertThat(failedNames).asList().contains(TEST_PACKAGE_2)
+ }
+
+ @Test
+ fun setPackagesSuspended_callerSuspendItself() {
+ val failedNames = suspendPackageHelper.setPackagesSuspended(arrayOf(DEVICE_OWNER_PACKAGE),
+ true /* suspended */, null /* appExtras */, null /* launcherExtras */,
+ null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid)
+
+ assertThat(failedNames).asList().hasSize(1)
+ assertThat(failedNames).asList().contains(DEVICE_OWNER_PACKAGE)
+ }
+
+ @Test
+ fun setPackagesSuspended_nonexistentPackage() {
+ val failedNames = suspendPackageHelper.setPackagesSuspended(arrayOf(NONEXISTENT_PACKAGE),
+ true /* suspended */, null /* appExtras */, null /* launcherExtras */,
+ null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid)
+
+ assertThat(failedNames).asList().hasSize(1)
+ assertThat(failedNames).asList().contains(NONEXISTENT_PACKAGE)
+ }
+
+ @Test
+ fun setPackagesSuspended_knownPackages() {
+ val knownPackages = arrayOf(DEVICE_ADMIN_PACKAGE, DEFAULT_HOME_PACKAGE, DIALER_PACKAGE,
+ INSTALLER_PACKAGE, UNINSTALLER_PACKAGE, VERIFIER_PACKAGE, PERMISSION_CONTROLLER_PACKAGE)
+ val failedNames = suspendPackageHelper.setPackagesSuspended(knownPackages,
+ true /* suspended */, null /* appExtras */, null /* launcherExtras */,
+ null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid)!!
+
+ assertThat(failedNames.size).isEqualTo(knownPackages.size)
+ for (pkg in knownPackages) {
+ assertThat(failedNames).asList().contains(pkg)
+ }
+ }
+
+ @Test
+ fun setPackagesUnsuspended() {
+ val targetPackages = arrayOf(TEST_PACKAGE_1, TEST_PACKAGE_2)
+ var failedNames = suspendPackageHelper.setPackagesSuspended(targetPackages,
+ true /* suspended */, null /* appExtras */, null /* launcherExtras */,
+ null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid)
+ testHandler.flush()
+ assertThat(failedNames).isEmpty()
+ failedNames = suspendPackageHelper.setPackagesSuspended(targetPackages,
+ false /* suspended */, null /* appExtras */, null /* launcherExtras */,
+ null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid)
+ testHandler.flush()
+
+ verify(pms, times(2)).scheduleWritePackageRestrictionsLocked(eq(TEST_USER_ID))
+ verify(broadcastHelper).sendPackageBroadcast(eq(Intent.ACTION_PACKAGES_UNSUSPENDED),
+ nullable(), bundleCaptor.capture(), anyInt(), nullable(), nullable(), any(),
+ nullable(), nullable(), nullable())
+ verify(broadcastHelper).doSendBroadcast(eq(Intent.ACTION_MY_PACKAGE_UNSUSPENDED),
+ nullable(), nullable(), any(), eq(TEST_PACKAGE_1), nullable(), any(), any(),
+ nullable(), nullable())
+ verify(broadcastHelper).doSendBroadcast(eq(Intent.ACTION_MY_PACKAGE_UNSUSPENDED),
+ nullable(), nullable(), any(), eq(TEST_PACKAGE_2), nullable(), any(), any(),
+ nullable(), nullable())
+
+ var modifiedPackages = bundleCaptor.value.getStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST)
+ assertThat(modifiedPackages).asList().containsExactly(TEST_PACKAGE_1, TEST_PACKAGE_2)
+ assertThat(failedNames).isEmpty()
+ }
+
+ @Test
+ fun getUnsuspendablePackagesForUser() {
+ val suspendables = arrayOf(TEST_PACKAGE_1, TEST_PACKAGE_2)
+ val unsuspendables = arrayOf(DEVICE_ADMIN_PACKAGE, DEFAULT_HOME_PACKAGE, DIALER_PACKAGE,
+ INSTALLER_PACKAGE, UNINSTALLER_PACKAGE, VERIFIER_PACKAGE, PERMISSION_CONTROLLER_PACKAGE)
+ val results = suspendPackageHelper.getUnsuspendablePackagesForUser(
+ suspendables + unsuspendables, TEST_USER_ID, deviceOwnerUid)
+
+ assertThat(results.size).isEqualTo(unsuspendables.size)
+ for (pkg in unsuspendables) {
+ assertThat(results).asList().contains(pkg)
+ }
+ }
+
+ @Test
+ fun getUnsuspendablePackagesForUser_callerIsNotAllowed() {
+ val suspendables = arrayOf(TEST_PACKAGE_1, TEST_PACKAGE_2)
+ val results = suspendPackageHelper.getUnsuspendablePackagesForUser(
+ suspendables, TEST_USER_ID, Binder.getCallingUid())
+
+ assertThat(results.size).isEqualTo(suspendables.size)
+ for (pkg in suspendables) {
+ assertThat(results).asList().contains(pkg)
+ }
+ }
+
+ @Test
+ fun getSuspendedPackageAppExtras() {
+ val appExtras = PersistableBundle()
+ appExtras.putString(TEST_PACKAGE_1, TEST_PACKAGE_1)
+ var failedNames = suspendPackageHelper.setPackagesSuspended(arrayOf(TEST_PACKAGE_1),
+ true /* suspended */, appExtras, null /* launcherExtras */,
+ null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid)
+ testHandler.flush()
+ assertThat(failedNames).isEmpty()
+
+ val result = suspendPackageHelper.getSuspendedPackageAppExtras(
+ TEST_PACKAGE_1, TEST_USER_ID, deviceOwnerUid)!!
+
+ assertThat(result.getString(TEST_PACKAGE_1)).isEqualTo(TEST_PACKAGE_1)
+ }
+
+ @Test
+ fun removeSuspensionsBySuspendingPackage() {
+ val appExtras = PersistableBundle()
+ appExtras.putString(TEST_PACKAGE_1, TEST_PACKAGE_2)
+ val targetPackages = arrayOf(TEST_PACKAGE_1, TEST_PACKAGE_2)
+ var failedNames = suspendPackageHelper.setPackagesSuspended(targetPackages,
+ true /* suspended */, appExtras, null /* launcherExtras */,
+ null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid)
+ testHandler.flush()
+ assertThat(failedNames).isEmpty()
+ assertThat(suspendPackageHelper.getSuspendingPackage(
+ TEST_PACKAGE_1, TEST_USER_ID, deviceOwnerUid)).isEqualTo(DEVICE_OWNER_PACKAGE)
+ assertThat(suspendPackageHelper.getSuspendingPackage(
+ TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isEqualTo(DEVICE_OWNER_PACKAGE)
+ assertThat(suspendPackageHelper.getSuspendedPackageAppExtras(
+ TEST_PACKAGE_1, TEST_USER_ID, deviceOwnerUid)).isNotNull()
+ assertThat(suspendPackageHelper.getSuspendedPackageAppExtras(
+ TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isNotNull()
+
+ suspendPackageHelper.removeSuspensionsBySuspendingPackage(targetPackages,
+ { suspendingPackage -> suspendingPackage == DEVICE_OWNER_PACKAGE }, TEST_USER_ID)
+
+ testHandler.flush()
+ verify(pms, times(2)).scheduleWritePackageRestrictionsLocked(eq(TEST_USER_ID))
+ verify(broadcastHelper).sendPackageBroadcast(eq(Intent.ACTION_PACKAGES_UNSUSPENDED),
+ nullable(), bundleCaptor.capture(), anyInt(), nullable(), nullable(), any(),
+ nullable(), nullable(), nullable())
+ verify(broadcastHelper).doSendBroadcast(eq(Intent.ACTION_MY_PACKAGE_UNSUSPENDED),
+ nullable(), nullable(), any(), eq(TEST_PACKAGE_1), nullable(), any(), any(),
+ nullable(), nullable())
+ verify(broadcastHelper).doSendBroadcast(eq(Intent.ACTION_MY_PACKAGE_UNSUSPENDED),
+ nullable(), nullable(), any(), eq(TEST_PACKAGE_2), nullable(), any(), any(),
+ nullable(), nullable())
+
+ assertThat(suspendPackageHelper.getSuspendingPackage(
+ TEST_PACKAGE_1, TEST_USER_ID, deviceOwnerUid)).isNull()
+ assertThat(suspendPackageHelper.getSuspendingPackage(
+ TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isNull()
+ assertThat(suspendPackageHelper.getSuspendedPackageAppExtras(
+ TEST_PACKAGE_1, TEST_USER_ID, deviceOwnerUid)).isNull()
+ assertThat(suspendPackageHelper.getSuspendedPackageAppExtras(
+ TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isNull()
+ }
+
+ @Test
+ fun getSuspendedPackageLauncherExtras() {
+ val launcherExtras = PersistableBundle()
+ launcherExtras.putString(TEST_PACKAGE_2, TEST_PACKAGE_2)
+ var failedNames = suspendPackageHelper.setPackagesSuspended(arrayOf(TEST_PACKAGE_2),
+ true /* suspended */, null /* appExtras */, launcherExtras,
+ null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid)
+ testHandler.flush()
+ assertThat(failedNames).isEmpty()
+
+ val result = suspendPackageHelper.getSuspendedPackageLauncherExtras(
+ TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)!!
+
+ assertThat(result.getString(TEST_PACKAGE_2)).isEqualTo(TEST_PACKAGE_2)
+ }
+
+ @Test
+ fun isPackageSuspended() {
+ var failedNames = suspendPackageHelper.setPackagesSuspended(arrayOf(TEST_PACKAGE_1),
+ true /* suspended */, null /* appExtras */, null /* launcherExtras */,
+ null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid)
+ testHandler.flush()
+ assertThat(failedNames).isEmpty()
+
+ assertThat(suspendPackageHelper.isPackageSuspended(
+ TEST_PACKAGE_1, TEST_USER_ID, deviceOwnerUid)).isTrue()
+ }
+
+ @Test
+ fun getSuspendingPackage() {
+ val launcherExtras = PersistableBundle()
+ launcherExtras.putString(TEST_PACKAGE_2, TEST_PACKAGE_2)
+ var failedNames = suspendPackageHelper.setPackagesSuspended(arrayOf(TEST_PACKAGE_2),
+ true /* suspended */, null /* appExtras */, launcherExtras,
+ null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid)
+ testHandler.flush()
+ assertThat(failedNames).isEmpty()
+
+ assertThat(suspendPackageHelper.getSuspendingPackage(
+ TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isEqualTo(DEVICE_OWNER_PACKAGE)
+ }
+
+ @Test
+ fun getSuspendedDialogInfo() {
+ val dialogInfo = SuspendDialogInfo.Builder()
+ .setTitle(TEST_PACKAGE_1).build()
+ var failedNames = suspendPackageHelper.setPackagesSuspended(arrayOf(TEST_PACKAGE_1),
+ true /* suspended */, null /* appExtras */, null /* launcherExtras */,
+ dialogInfo, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid)
+ testHandler.flush()
+ assertThat(failedNames).isEmpty()
+
+ val result = suspendPackageHelper.getSuspendedDialogInfo(
+ TEST_PACKAGE_1, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid)!!
+
+ assertThat(result.title).isEqualTo(TEST_PACKAGE_1)
+ }
+
+ @Test
+ @Throws(Exception::class)
+ fun sendPackagesSuspendedForUser_withSameVisibilityAllowList() {
+ mockAllowList(packageSetting1, allowList(10001, 10002, 10003))
+ mockAllowList(packageSetting2, allowList(10001, 10002, 10003))
+
+ suspendPackageHelper.sendPackagesSuspendedForUser(Intent.ACTION_PACKAGES_SUSPENDED,
+ packagesToSuspend, uidsToSuspend, TEST_USER_ID)
+ testHandler.flush()
+ verify(broadcastHelper).sendPackageBroadcast(any(), nullable(), bundleCaptor.capture(),
+ anyInt(), nullable(), nullable(), any(), nullable(), any(), nullable())
+
+ var changedPackages = bundleCaptor.value.getStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST)
+ var changedUids = bundleCaptor.value.getIntArray(Intent.EXTRA_CHANGED_UID_LIST)
+ assertThat(changedPackages).asList().containsExactly(TEST_PACKAGE_1, TEST_PACKAGE_2)
+ assertThat(changedUids).asList().containsExactly(
+ packageSetting1.appId, packageSetting2.appId)
+ }
+
+ @Test
+ @Throws(Exception::class)
+ fun sendPackagesSuspendedForUser_withDifferentVisibilityAllowList() {
+ mockAllowList(packageSetting1, allowList(10001, 10002, 10003))
+ mockAllowList(packageSetting2, allowList(10001, 10002, 10007))
+
+ suspendPackageHelper.sendPackagesSuspendedForUser(Intent.ACTION_PACKAGES_SUSPENDED,
+ packagesToSuspend, uidsToSuspend, TEST_USER_ID)
+ testHandler.flush()
+ verify(broadcastHelper, times(2)).sendPackageBroadcast(
+ any(), nullable(), bundleCaptor.capture(), anyInt(), nullable(), nullable(), any(),
+ nullable(), any(), nullable())
+
+ bundleCaptor.allValues.forEach {
+ var changedPackages = it.getStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST)
+ var changedUids = it.getIntArray(Intent.EXTRA_CHANGED_UID_LIST)
+ assertThat(changedPackages?.size).isEqualTo(1)
+ assertThat(changedUids?.size).isEqualTo(1)
+ assertThat(changedPackages?.get(0)).isAnyOf(TEST_PACKAGE_1, TEST_PACKAGE_2)
+ assertThat(changedUids?.get(0)).isAnyOf(packageSetting1.appId, packageSetting2.appId)
+ }
+ }
+
+ @Test
+ @Throws(Exception::class)
+ fun sendPackagesSuspendedForUser_withNullVisibilityAllowList() {
+ mockAllowList(packageSetting1, allowList(10001, 10002, 10003))
+ mockAllowList(packageSetting2, null)
+
+ suspendPackageHelper.sendPackagesSuspendedForUser(Intent.ACTION_PACKAGES_SUSPENDED,
+ packagesToSuspend, uidsToSuspend, TEST_USER_ID)
+ testHandler.flush()
+ verify(broadcastHelper, times(2)).sendPackageBroadcast(
+ any(), nullable(), bundleCaptor.capture(), anyInt(), nullable(), nullable(), any(),
+ nullable(), nullable(), nullable())
+
+ bundleCaptor.allValues.forEach {
+ var changedPackages = it.getStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST)
+ var changedUids = it.getIntArray(Intent.EXTRA_CHANGED_UID_LIST)
+ assertThat(changedPackages?.size).isEqualTo(1)
+ assertThat(changedUids?.size).isEqualTo(1)
+ assertThat(changedPackages?.get(0)).isAnyOf(TEST_PACKAGE_1, TEST_PACKAGE_2)
+ assertThat(changedUids?.get(0)).isAnyOf(packageSetting1.appId, packageSetting2.appId)
+ }
+ }
+
+ @Test
+ @Throws(Exception::class)
+ fun sendPackagesSuspendModifiedForUser() {
+ suspendPackageHelper.sendPackagesSuspendedForUser(Intent.ACTION_PACKAGES_SUSPENSION_CHANGED,
+ packagesToSuspend, uidsToSuspend, TEST_USER_ID)
+ testHandler.flush()
+ verify(broadcastHelper).sendPackageBroadcast(
+ eq(Intent.ACTION_PACKAGES_SUSPENSION_CHANGED), nullable(), bundleCaptor.capture(),
+ anyInt(), nullable(), nullable(), any(), nullable(), nullable(), nullable())
+
+ var modifiedPackages = bundleCaptor.value.getStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST)
+ var modifiedUids = bundleCaptor.value.getIntArray(Intent.EXTRA_CHANGED_UID_LIST)
+ assertThat(modifiedPackages).asList().containsExactly(TEST_PACKAGE_1, TEST_PACKAGE_2)
+ assertThat(modifiedUids).asList().containsExactly(
+ packageSetting1.appId, packageSetting2.appId)
+ }
+
+ private fun allowList(vararg uids: Int) = SparseArray<IntArray>().apply {
+ this.put(TEST_USER_ID, uids)
+ }
+
+ private fun mockAllowList(pkgSetting: PackageStateInternal, list: SparseArray<IntArray>?) {
+ whenever(rule.mocks().appsFilter.getVisibilityAllowList(
+ argThat { it?.packageName == pkgSetting.packageName }, any(IntArray::class.java),
+ any() as ArrayMap<String, out PackageStateInternal>
+ ))
+ .thenReturn(list)
+ }
+
+ private fun mockKnownPackages(pms: PackageManagerService) {
+ Mockito.doAnswer { it.arguments[0] == DEVICE_ADMIN_PACKAGE }.`when`(pms)
+ .isPackageDeviceAdmin(any(), any())
+ Mockito.doReturn(DEFAULT_HOME_PACKAGE).`when`(defaultAppProvider)
+ .getDefaultHome(eq(TEST_USER_ID))
+ Mockito.doReturn(DIALER_PACKAGE).`when`(defaultAppProvider)
+ .getDefaultDialer(eq(TEST_USER_ID))
+ Mockito.doReturn(arrayOf(INSTALLER_PACKAGE)).`when`(pms).getKnownPackageNamesInternal(
+ eq(PackageManagerInternal.PACKAGE_INSTALLER), eq(TEST_USER_ID))
+ Mockito.doReturn(arrayOf(UNINSTALLER_PACKAGE)).`when`(pms).getKnownPackageNamesInternal(
+ eq(PackageManagerInternal.PACKAGE_UNINSTALLER), eq(TEST_USER_ID))
+ Mockito.doReturn(arrayOf(VERIFIER_PACKAGE)).`when`(pms).getKnownPackageNamesInternal(
+ eq(PackageManagerInternal.PACKAGE_VERIFIER), eq(TEST_USER_ID))
+ Mockito.doReturn(arrayOf(PERMISSION_CONTROLLER_PACKAGE)).`when`(pms)
+ .getKnownPackageNamesInternal(
+ eq(PackageManagerInternal.PACKAGE_PERMISSION_CONTROLLER), eq(TEST_USER_ID))
+ }
+
+ private fun createPackageManagerService(vararg stageExistingPackages: String):
+ PackageManagerService {
+ stageExistingPackages.forEach {
+ rule.system().stageScanExistingPackage(it, 1L,
+ rule.system().dataAppDirectory)
+ }
+ var pms = PackageManagerService(rule.mocks().injector,
+ false /*coreOnly*/,
+ false /*factoryTest*/,
+ MockSystem.DEFAULT_VERSION_INFO.fingerprint,
+ false /*isEngBuild*/,
+ false /*isUserDebugBuild*/,
+ Build.VERSION_CODES.CUR_DEVELOPMENT,
+ Build.VERSION.INCREMENTAL,
+ false /*snapshotEnabled*/)
+ rule.system().validateFinalState()
+ return pms
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackagesBroadcastTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackagesBroadcastTest.kt
deleted file mode 100644
index 02ee35b9e7a8..000000000000
--- a/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackagesBroadcastTest.kt
+++ /dev/null
@@ -1,184 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.pm
-
-import android.content.Intent
-import android.os.Build
-import android.os.Bundle
-import android.util.ArrayMap
-import android.util.SparseArray
-import com.android.server.pm.pkg.PackageStateInternal
-import com.android.server.testutils.any
-import com.android.server.testutils.eq
-import com.android.server.testutils.nullable
-import com.android.server.testutils.whenever
-import com.google.common.truth.Truth.assertThat
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
-import org.mockito.ArgumentCaptor
-import org.mockito.ArgumentMatchers.anyInt
-import org.mockito.Captor
-import org.mockito.Mockito.argThat
-import org.mockito.Mockito.spy
-import org.mockito.Mockito.times
-import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
-
-@RunWith(JUnit4::class)
-class SuspendPackagesBroadcastTest {
-
- companion object {
- const val TEST_PACKAGE_1 = "com.android.test.package1"
- const val TEST_PACKAGE_2 = "com.android.test.package2"
- const val TEST_USER_ID = 0
- }
-
- lateinit var pms: PackageManagerService
- lateinit var packageSetting1: PackageStateInternal
- lateinit var packageSetting2: PackageStateInternal
- lateinit var packagesToSuspend: Array<String>
- lateinit var uidsToSuspend: IntArray
-
- @Captor
- lateinit var bundleCaptor: ArgumentCaptor<Bundle>
-
- @Rule
- @JvmField
- val rule = MockSystemRule()
-
- @Before
- @Throws(Exception::class)
- fun setup() {
- MockitoAnnotations.initMocks(this)
- rule.system().stageNominalSystemState()
- pms = spy(createPackageManagerService(TEST_PACKAGE_1, TEST_PACKAGE_2))
- packageSetting1 = pms.getPackageStateInternal(TEST_PACKAGE_1)!!
- packageSetting2 = pms.getPackageStateInternal(TEST_PACKAGE_2)!!
- packagesToSuspend = arrayOf(TEST_PACKAGE_1, TEST_PACKAGE_2)
- uidsToSuspend = intArrayOf(packageSetting1.appId, packageSetting2.appId)
- }
-
- @Test
- @Throws(Exception::class)
- fun sendPackagesSuspendedForUser_withSameVisibilityAllowList() {
- mockAllowList(packageSetting1, allowList(10001, 10002, 10003))
- mockAllowList(packageSetting2, allowList(10001, 10002, 10003))
-
- pms.sendPackagesSuspendedForUser(Intent.ACTION_PACKAGES_SUSPENDED,
- packagesToSuspend, uidsToSuspend, TEST_USER_ID)
- verify(pms).sendPackageBroadcast(any(), nullable(), bundleCaptor.capture(),
- anyInt(), nullable(), nullable(), any(), nullable(), any(), nullable())
-
- var changedPackages = bundleCaptor.value.getStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST)
- var changedUids = bundleCaptor.value.getIntArray(Intent.EXTRA_CHANGED_UID_LIST)
- assertThat(changedPackages).asList().containsExactly(TEST_PACKAGE_1, TEST_PACKAGE_2)
- assertThat(changedUids).asList().containsExactly(
- packageSetting1.appId, packageSetting2.appId)
- }
-
- @Test
- @Throws(Exception::class)
- fun sendPackagesSuspendedForUser_withDifferentVisibilityAllowList() {
- mockAllowList(packageSetting1, allowList(10001, 10002, 10003))
- mockAllowList(packageSetting2, allowList(10001, 10002, 10007))
-
- pms.sendPackagesSuspendedForUser(Intent.ACTION_PACKAGES_SUSPENDED,
- packagesToSuspend, uidsToSuspend, TEST_USER_ID)
- verify(pms, times(2)).sendPackageBroadcast(any(), nullable(), bundleCaptor.capture(),
- anyInt(), nullable(), nullable(), any(), nullable(), any(), nullable())
-
- bundleCaptor.allValues.forEach {
- var changedPackages = it.getStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST)
- var changedUids = it.getIntArray(Intent.EXTRA_CHANGED_UID_LIST)
- assertThat(changedPackages?.size).isEqualTo(1)
- assertThat(changedUids?.size).isEqualTo(1)
- assertThat(changedPackages?.get(0)).isAnyOf(TEST_PACKAGE_1, TEST_PACKAGE_2)
- assertThat(changedUids?.get(0)).isAnyOf(packageSetting1.appId, packageSetting2.appId)
- }
- }
-
- @Test
- @Throws(Exception::class)
- fun sendPackagesSuspendedForUser_withNullVisibilityAllowList() {
- mockAllowList(packageSetting1, allowList(10001, 10002, 10003))
- mockAllowList(packageSetting2, null)
-
- pms.sendPackagesSuspendedForUser(Intent.ACTION_PACKAGES_SUSPENDED,
- packagesToSuspend, uidsToSuspend, TEST_USER_ID)
- verify(pms, times(2)).sendPackageBroadcast(any(), nullable(), bundleCaptor.capture(),
- anyInt(), nullable(), nullable(), any(), nullable(), nullable(), nullable())
-
- bundleCaptor.allValues.forEach {
- var changedPackages = it.getStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST)
- var changedUids = it.getIntArray(Intent.EXTRA_CHANGED_UID_LIST)
- assertThat(changedPackages?.size).isEqualTo(1)
- assertThat(changedUids?.size).isEqualTo(1)
- assertThat(changedPackages?.get(0)).isAnyOf(TEST_PACKAGE_1, TEST_PACKAGE_2)
- assertThat(changedUids?.get(0)).isAnyOf(packageSetting1.appId, packageSetting2.appId)
- }
- }
-
- @Test
- @Throws(Exception::class)
- fun sendPackagesSuspendModifiedForUser() {
- pms.sendPackagesSuspendedForUser(Intent.ACTION_PACKAGES_SUSPENSION_CHANGED,
- packagesToSuspend, uidsToSuspend, TEST_USER_ID)
- verify(pms).sendPackageBroadcast(
- eq(Intent.ACTION_PACKAGES_SUSPENSION_CHANGED), nullable(), bundleCaptor.capture(),
- anyInt(), nullable(), nullable(), any(), nullable(), nullable(), nullable())
-
- var modifiedPackages = bundleCaptor.value.getStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST)
- var modifiedUids = bundleCaptor.value.getIntArray(Intent.EXTRA_CHANGED_UID_LIST)
- assertThat(modifiedPackages).asList().containsExactly(TEST_PACKAGE_1, TEST_PACKAGE_2)
- assertThat(modifiedUids).asList().containsExactly(
- packageSetting1.appId, packageSetting2.appId)
- }
-
- private fun allowList(vararg uids: Int) = SparseArray<IntArray>().apply {
- this.put(TEST_USER_ID, uids)
- }
-
- private fun mockAllowList(pkgSetting: PackageStateInternal, list: SparseArray<IntArray>?) {
- whenever(rule.mocks().appsFilter.getVisibilityAllowList(
- argThat { it?.packageName == pkgSetting.packageName }, any(IntArray::class.java),
- any() as ArrayMap<String, out PackageStateInternal>
- ))
- .thenReturn(list)
- }
-
- private fun createPackageManagerService(vararg stageExistingPackages: String):
- PackageManagerService {
- stageExistingPackages.forEach {
- rule.system().stageScanExistingPackage(it, 1L,
- rule.system().dataAppDirectory)
- }
- var pms = PackageManagerService(rule.mocks().injector,
- false /*coreOnly*/,
- false /*factoryTest*/,
- MockSystem.DEFAULT_VERSION_INFO.fingerprint,
- false /*isEngBuild*/,
- false /*isUserDebugBuild*/,
- Build.VERSION_CODES.CUR_DEVELOPMENT,
- Build.VERSION.INCREMENTAL,
- false /*snapshotEnabled*/)
- rule.system().validateFinalState()
- return pms
- }
-}
diff --git a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
index 966675819069..0b488b2add8e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
@@ -153,6 +153,9 @@ public class WallpaperManagerServiceTests {
sContext.getTestablePermissions().setPermission(
android.Manifest.permission.SET_WALLPAPER,
PackageManager.PERMISSION_GRANTED);
+ sContext.getTestablePermissions().setPermission(
+ android.Manifest.permission.SET_WALLPAPER_DIM_AMOUNT,
+ PackageManager.PERMISSION_GRANTED);
doNothing().when(sContext).sendBroadcastAsUser(any(), any());
//Wallpaper components
@@ -433,6 +436,15 @@ public class WallpaperManagerServiceTests {
assertTrue(timestamps[1] > timestamps[0]);
}
+ @Test
+ public void testSetWallpaperDimAmount() throws RemoteException {
+ mService.switchUser(USER_SYSTEM, null);
+ float dimAmount = 0.7f;
+ mService.setWallpaperDimAmount(dimAmount);
+ assertEquals("Getting dim amount should match after setting the dim amount",
+ mService.getWallpaperDimAmount(), dimAmount, 0.0);
+ }
+
// Verify that after continue switch user from userId 0 to lastUserId, the wallpaper data for
// non-current user must not bind to wallpaper service.
private void verifyNoConnectionBeforeLastUser(int lastUserId) {
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
index 36c37c4dbf2a..677f0f642e6e 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -541,11 +541,14 @@ public class ActivityManagerServiceTest {
| ActivityManager.UID_OBSERVER_CAPABILITY
};
final IUidObserver[] observers = new IUidObserver.Stub[changesToObserve.length];
+ doReturn(Process.myUid()).when(sPackageManagerInternal)
+ .getPackageUid(mContext.getOpPackageName(), 0 /* flags */, mContext.getUserId());
for (int i = 0; i < observers.length; ++i) {
observers[i] = mock(IUidObserver.Stub.class);
when(observers[i].asBinder()).thenReturn((IBinder) observers[i]);
mAms.registerUidObserver(observers[i], changesToObserve[i] /* which */,
- ActivityManager.PROCESS_STATE_UNKNOWN /* cutpoint */, null /* caller */);
+ ActivityManager.PROCESS_STATE_UNKNOWN /* cutpoint */,
+ mContext.getOpPackageName());
// When we invoke AMS.registerUidObserver, there are some interactions with observers[i]
// mock in RemoteCallbackList class. We don't want to test those interactions and
@@ -674,10 +677,12 @@ public class ActivityManagerServiceTest {
mockNoteOperation();
final IUidObserver observer = mock(IUidObserver.Stub.class);
-
when(observer.asBinder()).thenReturn((IBinder) observer);
+ doReturn(Process.myUid()).when(sPackageManagerInternal)
+ .getPackageUid(mContext.getOpPackageName(), 0 /* flags */, mContext.getUserId());
mAms.registerUidObserver(observer, ActivityManager.UID_OBSERVER_PROCSTATE /* which */,
- ActivityManager.PROCESS_STATE_SERVICE /* cutpoint */, null /* callingPackage */);
+ ActivityManager.PROCESS_STATE_SERVICE /* cutpoint */,
+ mContext.getOpPackageName());
// When we invoke AMS.registerUidObserver, there are some interactions with observer
// mock in RemoteCallbackList class. We don't want to test those interactions and
// at the same time, we don't want those to interfere with verifyNoMoreInteractions.
@@ -771,7 +776,9 @@ public class ActivityManagerServiceTest {
final IUidObserver observer = mock(IUidObserver.Stub.class);
when(observer.asBinder()).thenReturn((IBinder) observer);
- mAms.registerUidObserver(observer, 0, 0, null);
+ doReturn(Process.myUid()).when(sPackageManagerInternal)
+ .getPackageUid(mContext.getOpPackageName(), 0 /* flags */, mContext.getUserId());
+ mAms.registerUidObserver(observer, 0, 0, mContext.getOpPackageName());
// Verify that when observers are registered, then validateUids is correctly updated.
addPendingUidChanges(pendingItemsForUids);
mAms.mUidObserverController.dispatchUidsChanged();
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java
new file mode 100644
index 000000000000..2b72fabe7cca
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.log;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyBoolean;
+import static org.mockito.Mockito.anyFloat;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyLong;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.hardware.Sensor;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.input.InputSensorInfo;
+import android.platform.test.annotations.Presubmit;
+import android.testing.TestableContext;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.server.biometrics.sensors.BaseClientMonitor;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@Presubmit
+@SmallTest
+public class BiometricLoggerTest {
+
+ private static final int DEFAULT_MODALITY = BiometricsProtoEnums.MODALITY_FINGERPRINT;
+ private static final int DEFAULT_ACTION = BiometricsProtoEnums.ACTION_AUTHENTICATE;
+ private static final int DEFAULT_CLIENT = BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT;
+
+ @Rule
+ public TestableContext mContext = new TestableContext(
+ InstrumentationRegistry.getInstrumentation().getContext());
+ @Mock
+ private BiometricFrameworkStatsLogger mSink;
+ @Mock
+ private SensorManager mSensorManager;
+ @Mock
+ private BaseClientMonitor mClient;
+
+ private BiometricLogger mLogger;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mContext.addMockSystemService(SensorManager.class, mSensorManager);
+ when(mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT)).thenReturn(
+ new Sensor(new InputSensorInfo("", "", 0, 0, Sensor.TYPE_LIGHT, 0, 0, 0, 0, 0, 0,
+ "", "", 0, 0, 0))
+ );
+ }
+
+ private BiometricLogger createLogger() {
+ return createLogger(DEFAULT_MODALITY, DEFAULT_ACTION, DEFAULT_CLIENT);
+ }
+
+ private BiometricLogger createLogger(int statsModality, int statsAction, int statsClient) {
+ return new BiometricLogger(statsModality, statsAction, statsClient, mSink, mSensorManager);
+ }
+
+ @Test
+ public void testAcquired() {
+ mLogger = createLogger();
+
+ final int acquiredInfo = 2;
+ final int vendorCode = 3;
+ final boolean isCrypto = true;
+ final int targetUserId = 9;
+
+ mLogger.logOnAcquired(mContext, acquiredInfo, vendorCode, isCrypto, targetUserId);
+
+ verify(mSink).acquired(
+ eq(DEFAULT_MODALITY), eq(DEFAULT_ACTION), eq(DEFAULT_CLIENT), anyBoolean(),
+ eq(acquiredInfo), eq(vendorCode), eq(isCrypto), eq(targetUserId));
+ }
+
+ @Test
+ public void testAuth() {
+ mLogger = createLogger();
+
+ final boolean authenticated = true;
+ final boolean requireConfirmation = false;
+ final boolean isCrypto = false;
+ final int targetUserId = 11;
+ final boolean isBiometricPrompt = true;
+
+ mLogger.logOnAuthenticated(mContext,
+ authenticated, requireConfirmation, isCrypto, targetUserId, isBiometricPrompt);
+
+ verify(mSink).authenticate(
+ eq(DEFAULT_MODALITY), eq(DEFAULT_ACTION), eq(DEFAULT_CLIENT), anyBoolean(),
+ anyLong(), eq(authenticated), anyInt(), eq(requireConfirmation), eq(isCrypto),
+ eq(targetUserId), eq(isBiometricPrompt), anyFloat());
+ }
+
+ @Test
+ public void testEnroll() {
+ mLogger = createLogger();
+
+ final int targetUserId = 4;
+ final long latency = 44;
+ final boolean enrollSuccessful = true;
+
+ mLogger.logOnEnrolled(targetUserId, latency, enrollSuccessful);
+
+ verify(mSink).enroll(
+ eq(DEFAULT_MODALITY), eq(DEFAULT_ACTION), eq(DEFAULT_CLIENT),
+ eq(targetUserId), eq(latency), eq(enrollSuccessful), anyFloat());
+ }
+
+ @Test
+ public void testError() {
+ mLogger = createLogger();
+
+ final int error = 7;
+ final int vendorCode = 11;
+ final boolean isCrypto = false;
+ final int targetUserId = 9;
+
+ mLogger.logOnError(mContext, error, vendorCode, isCrypto, targetUserId);
+
+ verify(mSink).error(
+ eq(DEFAULT_MODALITY), eq(DEFAULT_ACTION), eq(DEFAULT_CLIENT), anyBoolean(),
+ anyLong(), eq(error), eq(vendorCode), eq(isCrypto), eq(targetUserId));
+ }
+
+ @Test
+ public void testBadModalityActsDisabled() {
+ mLogger = createLogger(
+ BiometricsProtoEnums.MODALITY_UNKNOWN, DEFAULT_ACTION, DEFAULT_CLIENT);
+ testDisabledMetrics(true /* isBadConfig */);
+ }
+
+ @Test
+ public void testBadActionActsDisabled() {
+ mLogger = createLogger(
+ DEFAULT_MODALITY, BiometricsProtoEnums.ACTION_UNKNOWN, DEFAULT_CLIENT);
+ testDisabledMetrics(true /* isBadConfig */);
+ }
+
+ @Test
+ public void testDisableLogger() {
+ mLogger = createLogger();
+ testDisabledMetrics(false /* isBadConfig */);
+ }
+
+ private void testDisabledMetrics(boolean isBadConfig) {
+ mLogger.disableMetrics();
+ mLogger.logOnAcquired(mContext,
+ 0 /* acquiredInfo */,
+ 1 /* vendorCode */,
+ true /* isCrypto */,
+ 8 /* targetUserId */);
+ mLogger.logOnAuthenticated(mContext,
+ true /* authenticated */,
+ true /* requireConfirmation */,
+ false /* isCrypto */,
+ 4 /* targetUserId */,
+ true/* isBiometricPrompt */);
+ mLogger.logOnEnrolled(2 /* targetUserId */,
+ 10 /* latency */,
+ true /* enrollSuccessful */);
+ mLogger.logOnError(mContext,
+ 4 /* error */,
+ 0 /* vendorCode */,
+ false /* isCrypto */,
+ 6 /* targetUserId */);
+
+ verify(mSink, never()).acquired(
+ anyInt(), anyInt(), anyInt(), anyBoolean(),
+ anyInt(), anyInt(), anyBoolean(), anyInt());
+ verify(mSink, never()).authenticate(
+ anyInt(), anyInt(), anyInt(), anyBoolean(),
+ anyLong(), anyBoolean(), anyInt(), anyBoolean(),
+ anyBoolean(), anyInt(), anyBoolean(), anyFloat());
+ verify(mSink, never()).enroll(
+ anyInt(), anyInt(), anyInt(), anyInt(), anyLong(), anyBoolean(), anyFloat());
+ verify(mSink, never()).error(
+ anyInt(), anyInt(), anyInt(), anyBoolean(),
+ anyLong(), anyInt(), anyInt(), anyBoolean(), anyInt());
+
+ mLogger.logUnknownEnrollmentInFramework();
+ mLogger.logUnknownEnrollmentInHal();
+
+ verify(mSink, times(isBadConfig ? 0 : 1))
+ .reportUnknownTemplateEnrolledHal(eq(DEFAULT_MODALITY));
+ verify(mSink, times(isBadConfig ? 0 : 1))
+ .reportUnknownTemplateEnrolledFramework(eq(DEFAULT_MODALITY));
+ }
+
+ @Test
+ public void systemHealthBadHalTemplate() {
+ mLogger = createLogger();
+ mLogger.logUnknownEnrollmentInHal();
+ verify(mSink).reportUnknownTemplateEnrolledHal(eq(DEFAULT_MODALITY));
+ }
+
+ @Test
+ public void systemHealthBadFrameworkTemplate() {
+ mLogger = createLogger();
+ mLogger.logUnknownEnrollmentInFramework();
+ verify(mSink).reportUnknownTemplateEnrolledFramework(eq(DEFAULT_MODALITY));
+ }
+
+ @Test
+ public void testALSCallback() {
+ mLogger = createLogger();
+ final CallbackWithProbe<Probe> callback =
+ mLogger.createALSCallback(true /* startWithClient */);
+
+ callback.onClientStarted(mClient);
+ verify(mSensorManager).registerListener(any(), any(), anyInt());
+
+ callback.onClientFinished(mClient, true /* success */);
+ verify(mSensorManager).unregisterListener(any(SensorEventListener.class));
+ }
+
+ @Test
+ public void testALSCallbackDoesNotStart() {
+ mLogger = createLogger();
+ final CallbackWithProbe<Probe> callback =
+ mLogger.createALSCallback(false /* startWithClient */);
+
+ callback.onClientStarted(mClient);
+ callback.onClientFinished(mClient, true /* success */);
+ verify(mSensorManager, never()).registerListener(any(), any(), anyInt());
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/AcquisitionClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/AcquisitionClientTest.java
index a06a78288535..fc55a9f4cf80 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/AcquisitionClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/AcquisitionClientTest.java
@@ -52,7 +52,7 @@ public class AcquisitionClientTest {
@Mock
private ClientMonitorCallbackConverter mClientCallback;
@Mock
- private BaseClientMonitor.Callback mSchedulerCallback;
+ private ClientMonitorCallback mSchedulerCallback;
@Before
public void setUp() {
@@ -96,7 +96,7 @@ public class AcquisitionClientTest {
}
@Override
- public void start(@NonNull Callback callback) {
+ public void start(@NonNull ClientMonitorCallback callback) {
super.start(callback);
startHalOperation();
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BaseClientMonitorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BaseClientMonitorTest.java
new file mode 100644
index 000000000000..51d234d5afeb
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BaseClientMonitorTest.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.annotation.NonNull;
+import androidx.test.filters.SmallTest;
+
+import com.android.server.biometrics.log.BiometricLogger;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@Presubmit
+@SmallTest
+public class BaseClientMonitorTest {
+
+ @Mock
+ private Context mContext;
+ @Mock
+ private IBinder mToken;
+ private @Mock ClientMonitorCallbackConverter mListener;
+ @Mock
+ private BiometricLogger mLogger;
+ @Mock
+ private ClientMonitorCallback mCallback;
+
+ private TestClientMonitor mClientMonitor;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mClientMonitor = new TestClientMonitor();
+ }
+
+ @Test
+ public void preparesForDeath() throws RemoteException {
+ verify(mToken).linkToDeath(eq(mClientMonitor), anyInt());
+
+ mClientMonitor.binderDied();
+
+ assertThat(mClientMonitor.mCanceled).isTrue();
+ assertThat(mClientMonitor.getListener()).isNull();
+ }
+
+ @Test
+ public void ignoresDeathWhenDone() {
+ mClientMonitor.markAlreadyDone();
+ mClientMonitor.binderDied();
+
+ assertThat(mClientMonitor.mCanceled).isFalse();
+ }
+
+ @Test
+ public void start() {
+ mClientMonitor.start(mCallback);
+
+ verify(mCallback).onClientStarted(eq(mClientMonitor));
+ }
+
+ @Test
+ public void destroy() {
+ mClientMonitor.destroy();
+ mClientMonitor.destroy();
+
+ assertThat(mClientMonitor.isAlreadyDone()).isTrue();
+ verify(mToken).unlinkToDeath(eq(mClientMonitor), anyInt());
+ }
+
+ @Test
+ public void hasRequestId() {
+ assertThat(mClientMonitor.hasRequestId()).isFalse();
+
+ final int id = 200;
+ mClientMonitor.setRequestId(id);
+ assertThat(mClientMonitor.hasRequestId()).isTrue();
+ assertThat(mClientMonitor.getRequestId()).isEqualTo(id);
+ }
+
+ private class TestClientMonitor extends BaseClientMonitor implements Interruptable {
+ public boolean mCanceled = false;
+
+ TestClientMonitor() {
+ super(mContext, mToken, mListener, 9 /* userId */, "foo" /* owner */, 2 /* cookie */,
+ 5 /* sensorId */, mLogger);
+ }
+
+ @Override
+ public int getProtoEnum() {
+ return 0;
+ }
+
+ @Override
+ public void cancel() {
+ mCanceled = true;
+ }
+
+ @Override
+ public void cancelWithoutStarting(@NonNull ClientMonitorCallback callback) {
+ mCanceled = true;
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java
index d4bac2c0402d..8751cf3810bf 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java
@@ -61,11 +61,11 @@ public class BiometricSchedulerOperationTest {
@Mock
private InterruptableMonitor<FakeHal> mClientMonitor;
@Mock
- private BaseClientMonitor.Callback mClientCallback;
+ private ClientMonitorCallback mClientCallback;
@Mock
private FakeHal mHal;
@Captor
- ArgumentCaptor<BaseClientMonitor.Callback> mStartCallback;
+ ArgumentCaptor<ClientMonitorCallback> mStartCallback;
private Handler mHandler;
private BiometricSchedulerOperation mOperation;
@@ -89,7 +89,7 @@ public class BiometricSchedulerOperationTest {
assertThat(mOperation.isFinished()).isFalse();
final boolean started = mOperation.startWithCookie(
- mock(BaseClientMonitor.Callback.class), cookie);
+ mock(ClientMonitorCallback.class), cookie);
assertThat(started).isTrue();
verify(mClientMonitor).start(mStartCallback.capture());
@@ -106,7 +106,7 @@ public class BiometricSchedulerOperationTest {
assertThat(mOperation.isReadyToStart()).isEqualTo(goodCookie);
final boolean started = mOperation.startWithCookie(
- mock(BaseClientMonitor.Callback.class), badCookie);
+ mock(ClientMonitorCallback.class), badCookie);
assertThat(started).isFalse();
assertThat(mOperation.isStarted()).isFalse();
@@ -119,7 +119,7 @@ public class BiometricSchedulerOperationTest {
when(mClientMonitor.getCookie()).thenReturn(0);
when(mClientMonitor.getFreshDaemon()).thenReturn(mHal);
- final BaseClientMonitor.Callback cb = mock(BaseClientMonitor.Callback.class);
+ final ClientMonitorCallback cb = mock(ClientMonitorCallback.class);
mOperation.start(cb);
verify(mClientMonitor).start(mStartCallback.capture());
mStartCallback.getValue().onClientStarted(mClientMonitor);
@@ -146,7 +146,7 @@ public class BiometricSchedulerOperationTest {
when(mClientMonitor.getCookie()).thenReturn(0);
when(mClientMonitor.getFreshDaemon()).thenReturn(null);
- final BaseClientMonitor.Callback cb = mock(BaseClientMonitor.Callback.class);
+ final ClientMonitorCallback cb = mock(ClientMonitorCallback.class);
mOperation.start(cb);
verify(mClientMonitor, never()).start(any());
@@ -164,17 +164,17 @@ public class BiometricSchedulerOperationTest {
public void doesNotStartWithCookie() {
when(mClientMonitor.getCookie()).thenReturn(9);
assertThrows(IllegalStateException.class,
- () -> mOperation.start(mock(BaseClientMonitor.Callback.class)));
+ () -> mOperation.start(mock(ClientMonitorCallback.class)));
}
@Test
public void cannotRestart() {
when(mClientMonitor.getFreshDaemon()).thenReturn(mHal);
- mOperation.start(mock(BaseClientMonitor.Callback.class));
+ mOperation.start(mock(ClientMonitorCallback.class));
assertThrows(IllegalStateException.class,
- () -> mOperation.start(mock(BaseClientMonitor.Callback.class)));
+ () -> mOperation.start(mock(ClientMonitorCallback.class)));
}
@Test
@@ -187,14 +187,14 @@ public class BiometricSchedulerOperationTest {
verify(mClientMonitor).unableToStart();
verify(mClientMonitor).destroy();
assertThrows(IllegalStateException.class,
- () -> mOperation.start(mock(BaseClientMonitor.Callback.class)));
+ () -> mOperation.start(mock(ClientMonitorCallback.class)));
}
@Test
public void cannotAbortRunning() {
when(mClientMonitor.getFreshDaemon()).thenReturn(mHal);
- mOperation.start(mock(BaseClientMonitor.Callback.class));
+ mOperation.start(mock(ClientMonitorCallback.class));
assertThrows(IllegalStateException.class, () -> mOperation.abort());
}
@@ -203,8 +203,8 @@ public class BiometricSchedulerOperationTest {
public void cancel() {
when(mClientMonitor.getFreshDaemon()).thenReturn(mHal);
- final BaseClientMonitor.Callback startCb = mock(BaseClientMonitor.Callback.class);
- final BaseClientMonitor.Callback cancelCb = mock(BaseClientMonitor.Callback.class);
+ final ClientMonitorCallback startCb = mock(ClientMonitorCallback.class);
+ final ClientMonitorCallback cancelCb = mock(ClientMonitorCallback.class);
mOperation.start(startCb);
verify(mClientMonitor).start(mStartCallback.capture());
mStartCallback.getValue().onClientStarted(mClientMonitor);
@@ -230,12 +230,12 @@ public class BiometricSchedulerOperationTest {
public void cancelWithoutStarting() {
when(mClientMonitor.getFreshDaemon()).thenReturn(mHal);
- final BaseClientMonitor.Callback cancelCb = mock(BaseClientMonitor.Callback.class);
+ final ClientMonitorCallback cancelCb = mock(ClientMonitorCallback.class);
mOperation.cancel(mHandler, cancelCb);
assertThat(mOperation.isCanceling()).isTrue();
- ArgumentCaptor<BaseClientMonitor.Callback> cbCaptor =
- ArgumentCaptor.forClass(BaseClientMonitor.Callback.class);
+ ArgumentCaptor<ClientMonitorCallback> cbCaptor =
+ ArgumentCaptor.forClass(ClientMonitorCallback.class);
verify(mClientMonitor).cancelWithoutStarting(cbCaptor.capture());
cbCaptor.getValue().onClientFinished(mClientMonitor, true);
@@ -278,7 +278,7 @@ public class BiometricSchedulerOperationTest {
}
mOperation.markCanceling();
- final BaseClientMonitor.Callback cb = mock(BaseClientMonitor.Callback.class);
+ final ClientMonitorCallback cb = mock(ClientMonitorCallback.class);
if (withCookie != null) {
mOperation.startWithCookie(cb, withCookie);
} else {
@@ -307,12 +307,12 @@ public class BiometricSchedulerOperationTest {
private void cancelWatchdog(boolean start) {
when(mClientMonitor.getFreshDaemon()).thenReturn(mHal);
- mOperation.start(mock(BaseClientMonitor.Callback.class));
+ mOperation.start(mock(ClientMonitorCallback.class));
if (start) {
verify(mClientMonitor).start(mStartCallback.capture());
mStartCallback.getValue().onClientStarted(mClientMonitor);
}
- mOperation.cancel(mHandler, mock(BaseClientMonitor.Callback.class));
+ mOperation.cancel(mHandler, mock(ClientMonitorCallback.class));
assertThat(mOperation.isCanceling()).isTrue();
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
index ac0831983262..c99d656892f4 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
@@ -114,13 +114,13 @@ public class BiometricSchedulerTest {
final TestHalClientMonitor client2 = new TestHalClientMonitor(
mContext, mToken, () -> mock(Object.class));
- final BaseClientMonitor.Callback callback1 = mock(BaseClientMonitor.Callback.class);
- final BaseClientMonitor.Callback callback2 = mock(BaseClientMonitor.Callback.class);
+ final ClientMonitorCallback callback1 = mock(ClientMonitorCallback.class);
+ final ClientMonitorCallback callback2 = mock(ClientMonitorCallback.class);
// Pretend the scheduler is busy so the first operation doesn't start right away. We want
// to pretend like there are two operations in the queue before kicking things off
mScheduler.mCurrentOperation = new BiometricSchedulerOperation(
- mock(BaseClientMonitor.class), mock(BaseClientMonitor.Callback.class));
+ mock(BaseClientMonitor.class), mock(ClientMonitorCallback.class));
mScheduler.scheduleClientMonitor(client1, callback1);
assertEquals(1, mScheduler.mPendingOperations.size());
@@ -152,13 +152,13 @@ public class BiometricSchedulerTest {
final TestHalClientMonitor client2 =
new TestHalClientMonitor(mContext, mToken, () -> daemon2);
- final BaseClientMonitor.Callback callback1 = mock(BaseClientMonitor.Callback.class);
- final BaseClientMonitor.Callback callback2 = mock(BaseClientMonitor.Callback.class);
+ final ClientMonitorCallback callback1 = mock(ClientMonitorCallback.class);
+ final ClientMonitorCallback callback2 = mock(ClientMonitorCallback.class);
// Pretend the scheduler is busy so the first operation doesn't start right away. We want
// to pretend like there are two operations in the queue before kicking things off
mScheduler.mCurrentOperation = new BiometricSchedulerOperation(
- mock(BaseClientMonitor.class), mock(BaseClientMonitor.Callback.class));
+ mock(BaseClientMonitor.class), mock(ClientMonitorCallback.class));
mScheduler.scheduleClientMonitor(client1, callback1);
assertEquals(1, mScheduler.mPendingOperations.size());
@@ -187,7 +187,7 @@ public class BiometricSchedulerTest {
final HalClientMonitor.LazyDaemon<Object> lazyDaemon1 = () -> mock(Object.class);
final TestAuthenticationClient client1 = new TestAuthenticationClient(mContext,
lazyDaemon1, mToken, mock(ClientMonitorCallbackConverter.class));
- final BaseClientMonitor.Callback callback1 = mock(BaseClientMonitor.Callback.class);
+ final ClientMonitorCallback callback1 = mock(ClientMonitorCallback.class);
// Schedule a BiometricPrompt authentication request
mScheduler.scheduleClientMonitor(client1, callback1);
@@ -628,7 +628,7 @@ public class BiometricSchedulerTest {
}
@Override
- public void start(@NonNull Callback callback) {
+ public void start(@NonNull ClientMonitorCallback callback) {
super.start(callback);
assertFalse(mStarted);
mStarted = true;
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/CompositeCallbackTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/CompositeCallbackTest.java
index 09b5c5cac466..587bb600f409 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/CompositeCallbackTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/CompositeCallbackTest.java
@@ -17,36 +17,62 @@
package com.android.server.biometrics.sensors;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
+import org.junit.Before;
import org.junit.Test;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Objects;
@Presubmit
@SmallTest
public class CompositeCallbackTest {
+ @Mock
+ private BaseClientMonitor mClientMonitor;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ }
+
@Test
- public void testNullCallback() {
- BaseClientMonitor.Callback callback1 = mock(BaseClientMonitor.Callback.class);
- BaseClientMonitor.Callback callback2 = mock(BaseClientMonitor.Callback.class);
- BaseClientMonitor.Callback callback3 = null;
+ public void testCallbacks() {
+ testCallbacks(mock(ClientMonitorCallback.class), mock(ClientMonitorCallback.class));
+ }
+
+ @Test
+ public void testNullCallbacks() {
+ testCallbacks(null, mock(ClientMonitorCallback.class),
+ null, mock(ClientMonitorCallback.class));
+ }
- BaseClientMonitor.CompositeCallback callback = new BaseClientMonitor.CompositeCallback(
- callback1, callback2, callback3);
+ private void testCallbacks(ClientMonitorCallback... callbacks) {
+ final ClientMonitorCallback[] expected = Arrays.stream(callbacks)
+ .filter(Objects::nonNull).toArray(ClientMonitorCallback[]::new);
- BaseClientMonitor clientMonitor = mock(BaseClientMonitor.class);
+ ClientMonitorCompositeCallback callback = new ClientMonitorCompositeCallback(callbacks);
- callback.onClientStarted(clientMonitor);
- verify(callback1).onClientStarted(eq(clientMonitor));
- verify(callback2).onClientStarted(eq(clientMonitor));
+ callback.onClientStarted(mClientMonitor);
+ final InOrder order = inOrder(expected);
+ for (ClientMonitorCallback cb : expected) {
+ order.verify(cb).onClientStarted(eq(mClientMonitor));
+ }
- callback.onClientFinished(clientMonitor, true /* success */);
- verify(callback1).onClientFinished(eq(clientMonitor), eq(true));
- verify(callback2).onClientFinished(eq(clientMonitor), eq(true));
+ callback.onClientFinished(mClientMonitor, true /* success */);
+ Collections.reverse(Arrays.asList(expected));
+ for (ClientMonitorCallback cb : expected) {
+ order.verify(cb).onClientFinished(eq(mClientMonitor), eq(true));
+ }
}
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java
index 407f5fb04adf..a11709aff87f 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java
@@ -155,7 +155,7 @@ public class UserAwareBiometricSchedulerTest {
assertNull(mScheduler.mCurrentOperation);
final BiometricSchedulerOperation fakeOperation = new BiometricSchedulerOperation(
- mock(BaseClientMonitor.class), new BaseClientMonitor.Callback() {});
+ mock(BaseClientMonitor.class), new ClientMonitorCallback() {});
mScheduler.mCurrentOperation = fakeOperation;
startUserClient.mCallback.onClientFinished(startUserClient, true);
assertSame(fakeOperation, mScheduler.mCurrentOperation);
@@ -234,7 +234,7 @@ public class UserAwareBiometricSchedulerTest {
}
@Override
- public void start(@NonNull Callback callback) {
+ public void start(@NonNull ClientMonitorCallback callback) {
super.start(callback);
onUserStopped();
}
@@ -248,7 +248,7 @@ public class UserAwareBiometricSchedulerTest {
private static class TestStartUserClient extends StartUserClient<Object, Object> {
private final boolean mShouldFinish;
- Callback mCallback;
+ ClientMonitorCallback mCallback;
public TestStartUserClient(@NonNull Context context,
@NonNull LazyDaemon<Object> lazyDaemon, @Nullable IBinder token, int userId,
@@ -263,7 +263,7 @@ public class UserAwareBiometricSchedulerTest {
}
@Override
- public void start(@NonNull Callback callback) {
+ public void start(@NonNull ClientMonitorCallback callback) {
super.start(callback);
mCallback = callback;
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClientTest.java
index 55dc03595b3d..931fad14888e 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClientTest.java
@@ -34,7 +34,7 @@ import android.platform.test.annotations.Presubmit;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.filters.SmallTest;
-import com.android.server.biometrics.sensors.BaseClientMonitor;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import org.junit.Before;
@@ -61,7 +61,7 @@ public class FaceGenerateChallengeClientTest {
@Mock
private IFaceServiceReceiver mOtherReceiver;
@Mock
- private BaseClientMonitor.Callback mMonitorCallback;
+ private ClientMonitorCallback mMonitorCallback;
private FaceGenerateChallengeClient mClient;
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java
new file mode 100644
index 000000000000..53468c81a1e2
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.companion.virtual;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.verify;
+
+import android.hardware.input.InputManagerInternal;
+import android.os.Binder;
+import android.os.IBinder;
+import android.platform.test.annotations.Presubmit;
+import android.view.Display;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.LocalServices;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class InputControllerTest {
+
+ @Mock
+ private InputManagerInternal mInputManagerInternalMock;
+ @Mock
+ private InputController.NativeWrapper mNativeWrapperMock;
+
+ private InputController mInputController;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ doNothing().when(mInputManagerInternalMock).setVirtualMousePointerDisplayId(anyInt());
+ LocalServices.removeServiceForTest(InputManagerInternal.class);
+ LocalServices.addService(InputManagerInternal.class, mInputManagerInternalMock);
+
+ mInputController = new InputController(new Object(), mNativeWrapperMock);
+ }
+
+ @Test
+ public void unregisterInputDevice_allMiceUnregistered_unsetValues() {
+ final IBinder deviceToken = new Binder();
+ mInputController.createMouse("name", /*vendorId= */ 1, /*productId= */ 1, deviceToken,
+ /* displayId= */ 1);
+ mInputController.unregisterInputDevice(deviceToken);
+ verify(mInputManagerInternalMock).setVirtualMousePointerDisplayId(
+ eq(Display.INVALID_DISPLAY));
+ }
+
+ @Test
+ public void unregisterInputDevice_anotherMouseExists_setPointerDisplayIdOverride() {
+ final IBinder deviceToken = new Binder();
+ mInputController.createMouse("name", /*vendorId= */ 1, /*productId= */ 1, deviceToken,
+ /* displayId= */ 1);
+ verify(mInputManagerInternalMock).setVirtualMousePointerDisplayId(eq(1));
+ mInputController.createMouse("name", /*vendorId= */ 1, /*productId= */ 1, deviceToken,
+ /* displayId= */ 2);
+ verify(mInputManagerInternalMock).setVirtualMousePointerDisplayId(eq(2));
+ mInputController.unregisterInputDevice(deviceToken);
+ verify(mInputManagerInternalMock).setVirtualMousePointerDisplayId(eq(1));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
index 0a0f7d7a0ae0..72100e44b3eb 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
@@ -18,19 +18,24 @@ package com.android.server.companion.virtual;
import static com.google.common.truth.Truth.assertWithMessage;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doCallRealMethod;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import static org.testng.Assert.assertThrows;
import android.Manifest;
+import android.app.admin.DevicePolicyManager;
+import android.companion.virtual.IVirtualDeviceActivityListener;
import android.companion.virtual.VirtualDeviceParams;
import android.content.Context;
import android.content.ContextWrapper;
import android.graphics.Point;
import android.hardware.display.DisplayManagerInternal;
+import android.hardware.input.InputManagerInternal;
import android.hardware.input.VirtualKeyEvent;
import android.hardware.input.VirtualMouseButtonEvent;
import android.hardware.input.VirtualMouseRelativeEvent;
@@ -73,6 +78,12 @@ public class VirtualDeviceManagerServiceTest {
private DisplayManagerInternal mDisplayManagerInternalMock;
@Mock
private VirtualDeviceImpl.PendingTrampolineCallback mPendingTrampolineCallback;
+ @Mock
+ private DevicePolicyManager mDevicePolicyManagerMock;
+ @Mock
+ private InputManagerInternal mInputManagerInternalMock;
+ @Mock
+ private IVirtualDeviceActivityListener mActivityListener;
@Before
public void setUp() {
@@ -81,17 +92,32 @@ public class VirtualDeviceManagerServiceTest {
LocalServices.removeServiceForTest(DisplayManagerInternal.class);
LocalServices.addService(DisplayManagerInternal.class, mDisplayManagerInternalMock);
+ doNothing().when(mInputManagerInternalMock).setVirtualMousePointerDisplayId(anyInt());
+ LocalServices.removeServiceForTest(InputManagerInternal.class);
+ LocalServices.addService(InputManagerInternal.class, mInputManagerInternalMock);
+
mContext = Mockito.spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
doNothing().when(mContext).enforceCallingOrSelfPermission(
eq(Manifest.permission.CREATE_VIRTUAL_DEVICE), anyString());
+ when(mContext.getSystemService(Context.DEVICE_POLICY_SERVICE)).thenReturn(
+ mDevicePolicyManagerMock);
+
mInputController = new InputController(new Object(), mNativeWrapperMock);
mDeviceImpl = new VirtualDeviceImpl(mContext,
/* association info */ null, new Binder(), /* uid */ 0, mInputController,
- (int associationId) -> {}, mPendingTrampolineCallback,
+ (int associationId) -> {}, mPendingTrampolineCallback, mActivityListener,
new VirtualDeviceParams.Builder().build());
}
@Test
+ public void onVirtualDisplayRemovedLocked_doesNotThrowException() {
+ final int displayId = 2;
+ mDeviceImpl.onVirtualDisplayCreatedLocked(displayId);
+ // This call should not throw any exceptions.
+ mDeviceImpl.onVirtualDisplayRemovedLocked(displayId);
+ }
+
+ @Test
public void createVirtualKeyboard_noDisplay_failsSecurityException() {
assertThrows(
SecurityException.class,
@@ -154,7 +180,7 @@ public class VirtualDeviceManagerServiceTest {
mDeviceImpl.createVirtualKeyboard(DISPLAY_ID, DEVICE_NAME, VENDOR_ID, PRODUCT_ID,
BINDER);
assertWithMessage("Virtual keyboard should register fd when the display matches")
- .that(mInputController.mInputDeviceFds).isNotEmpty();
+ .that(mInputController.mInputDeviceDescriptors).isNotEmpty();
verify(mNativeWrapperMock).openUinputKeyboard(DEVICE_NAME, VENDOR_ID, PRODUCT_ID);
}
@@ -164,7 +190,7 @@ public class VirtualDeviceManagerServiceTest {
mDeviceImpl.createVirtualMouse(DISPLAY_ID, DEVICE_NAME, VENDOR_ID, PRODUCT_ID,
BINDER);
assertWithMessage("Virtual keyboard should register fd when the display matches")
- .that(mInputController.mInputDeviceFds).isNotEmpty();
+ .that(mInputController.mInputDeviceDescriptors).isNotEmpty();
verify(mNativeWrapperMock).openUinputMouse(DEVICE_NAME, VENDOR_ID, PRODUCT_ID);
}
@@ -174,7 +200,7 @@ public class VirtualDeviceManagerServiceTest {
mDeviceImpl.createVirtualTouchscreen(DISPLAY_ID, DEVICE_NAME, VENDOR_ID, PRODUCT_ID,
BINDER, new Point(WIDTH, HEIGHT));
assertWithMessage("Virtual keyboard should register fd when the display matches")
- .that(mInputController.mInputDeviceFds).isNotEmpty();
+ .that(mInputController.mInputDeviceDescriptors).isNotEmpty();
verify(mNativeWrapperMock).openUinputTouchscreen(DEVICE_NAME, VENDOR_ID, PRODUCT_ID, HEIGHT,
WIDTH);
}
@@ -194,7 +220,9 @@ public class VirtualDeviceManagerServiceTest {
final int fd = 1;
final int keyCode = KeyEvent.KEYCODE_A;
final int action = VirtualKeyEvent.ACTION_UP;
- mInputController.mInputDeviceFds.put(BINDER, fd);
+ mInputController.mInputDeviceDescriptors.put(BINDER,
+ new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 1,
+ /* displayId= */ 1));
mDeviceImpl.sendKeyEvent(BINDER, new VirtualKeyEvent.Builder().setKeyCode(keyCode)
.setAction(action).build());
verify(mNativeWrapperMock).writeKeyEvent(fd, keyCode, action);
@@ -217,7 +245,10 @@ public class VirtualDeviceManagerServiceTest {
final int fd = 1;
final int buttonCode = VirtualMouseButtonEvent.BUTTON_BACK;
final int action = VirtualMouseButtonEvent.ACTION_BUTTON_PRESS;
- mInputController.mInputDeviceFds.put(BINDER, fd);
+ mInputController.mInputDeviceDescriptors.put(BINDER,
+ new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 2,
+ /* displayId= */ 1));
+ mInputController.mActivePointerDisplayId = 1;
mDeviceImpl.sendButtonEvent(BINDER, new VirtualMouseButtonEvent.Builder()
.setButtonCode(buttonCode)
.setAction(action).build());
@@ -225,6 +256,22 @@ public class VirtualDeviceManagerServiceTest {
}
@Test
+ public void sendButtonEvent_hasFd_wrongDisplay_throwsIllegalStateException() {
+ final int fd = 1;
+ final int buttonCode = VirtualMouseButtonEvent.BUTTON_BACK;
+ final int action = VirtualMouseButtonEvent.ACTION_BUTTON_PRESS;
+ mInputController.mInputDeviceDescriptors.put(BINDER,
+ new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 2,
+ /* displayId= */ 1));
+ assertThrows(
+ IllegalStateException.class,
+ () ->
+ mDeviceImpl.sendButtonEvent(BINDER, new VirtualMouseButtonEvent.Builder()
+ .setButtonCode(buttonCode)
+ .setAction(action).build()));
+ }
+
+ @Test
public void sendRelativeEvent_noFd() {
assertThrows(
IllegalArgumentException.class,
@@ -239,13 +286,32 @@ public class VirtualDeviceManagerServiceTest {
final int fd = 1;
final float x = -0.2f;
final float y = 0.7f;
- mInputController.mInputDeviceFds.put(BINDER, fd);
+ mInputController.mInputDeviceDescriptors.put(BINDER,
+ new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 2,
+ /* displayId= */ 1));
+ mInputController.mActivePointerDisplayId = 1;
mDeviceImpl.sendRelativeEvent(BINDER, new VirtualMouseRelativeEvent.Builder()
.setRelativeX(x).setRelativeY(y).build());
verify(mNativeWrapperMock).writeRelativeEvent(fd, x, y);
}
@Test
+ public void sendRelativeEvent_hasFd_wrongDisplay_throwsIllegalStateException() {
+ final int fd = 1;
+ final float x = -0.2f;
+ final float y = 0.7f;
+ mInputController.mInputDeviceDescriptors.put(BINDER,
+ new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 2,
+ /* displayId= */ 1));
+ assertThrows(
+ IllegalStateException.class,
+ () ->
+ mDeviceImpl.sendRelativeEvent(BINDER,
+ new VirtualMouseRelativeEvent.Builder()
+ .setRelativeX(x).setRelativeY(y).build()));
+ }
+
+ @Test
public void sendScrollEvent_noFd() {
assertThrows(
IllegalArgumentException.class,
@@ -261,7 +327,10 @@ public class VirtualDeviceManagerServiceTest {
final int fd = 1;
final float x = 0.5f;
final float y = 1f;
- mInputController.mInputDeviceFds.put(BINDER, fd);
+ mInputController.mInputDeviceDescriptors.put(BINDER,
+ new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 2,
+ /* displayId= */ 1));
+ mInputController.mActivePointerDisplayId = 1;
mDeviceImpl.sendScrollEvent(BINDER, new VirtualMouseScrollEvent.Builder()
.setXAxisMovement(x)
.setYAxisMovement(y).build());
@@ -269,6 +338,22 @@ public class VirtualDeviceManagerServiceTest {
}
@Test
+ public void sendScrollEvent_hasFd_wrongDisplay_throwsIllegalStateException() {
+ final int fd = 1;
+ final float x = 0.5f;
+ final float y = 1f;
+ mInputController.mInputDeviceDescriptors.put(BINDER,
+ new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 2,
+ /* displayId= */ 1));
+ assertThrows(
+ IllegalStateException.class,
+ () ->
+ mDeviceImpl.sendScrollEvent(BINDER, new VirtualMouseScrollEvent.Builder()
+ .setXAxisMovement(x)
+ .setYAxisMovement(y).build()));
+ }
+
+ @Test
public void sendTouchEvent_noFd() {
assertThrows(
IllegalArgumentException.class,
@@ -290,7 +375,9 @@ public class VirtualDeviceManagerServiceTest {
final float x = 100.5f;
final float y = 200.5f;
final int action = VirtualTouchEvent.ACTION_UP;
- mInputController.mInputDeviceFds.put(BINDER, fd);
+ mInputController.mInputDeviceDescriptors.put(BINDER,
+ new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 3,
+ /* displayId= */ 1));
mDeviceImpl.sendTouchEvent(BINDER, new VirtualTouchEvent.Builder().setX(x)
.setY(y).setAction(action).setPointerId(pointerId).setToolType(toolType).build());
verify(mNativeWrapperMock).writeTouchEvent(fd, pointerId, toolType, action, x, y, Float.NaN,
@@ -307,7 +394,9 @@ public class VirtualDeviceManagerServiceTest {
final int action = VirtualTouchEvent.ACTION_UP;
final float pressure = 1.0f;
final float majorAxisSize = 10.0f;
- mInputController.mInputDeviceFds.put(BINDER, fd);
+ mInputController.mInputDeviceDescriptors.put(BINDER,
+ new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 3,
+ /* displayId= */ 1));
mDeviceImpl.sendTouchEvent(BINDER, new VirtualTouchEvent.Builder().setX(x)
.setY(y).setAction(action).setPointerId(pointerId).setToolType(toolType)
.setPressure(pressure).setMajorAxisSize(majorAxisSize).build());
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 1228d625325f..842a4381bc8a 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -18,7 +18,6 @@ package com.android.server.devicepolicy;
import static android.app.AppOpsManager.MODE_ALLOWED;
import static android.app.AppOpsManager.MODE_DEFAULT;
import static android.app.AppOpsManager.OP_ACTIVATE_VPN;
-import static android.app.Notification.EXTRA_TEXT;
import static android.app.Notification.EXTRA_TITLE;
import static android.app.admin.DevicePolicyManager.ACTION_CHECK_POLICY_COMPLIANCE;
import static android.app.admin.DevicePolicyManager.DELEGATION_APP_RESTRICTIONS;
@@ -34,12 +33,17 @@ import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_LOW;
import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_MEDIUM;
import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE;
import static android.app.admin.DevicePolicyManager.PRIVATE_DNS_SET_NO_ERROR;
+import static android.app.admin.DevicePolicyManager.WIFI_SECURITY_ENTERPRISE_192;
+import static android.app.admin.DevicePolicyManager.WIFI_SECURITY_ENTERPRISE_EAP;
+import static android.app.admin.DevicePolicyManager.WIFI_SECURITY_OPEN;
+import static android.app.admin.DevicePolicyManager.WIFI_SECURITY_PERSONAL;
import static android.app.admin.DevicePolicyManager.WIPE_EUICC;
import static android.app.admin.PasswordMetrics.computeForPasswordOrPin;
import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_DIRECT_BOOT_AWARE;
import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_DEFAULT;
import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE;
import static android.net.InetAddresses.parseNumericAddress;
+import static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_1;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
import static com.android.internal.widget.LockPatternUtils.EscrowTokenStateChangeCallback;
@@ -91,6 +95,7 @@ import android.app.admin.DevicePolicyManagerLiteInternal;
import android.app.admin.FactoryResetProtectionPolicy;
import android.app.admin.PasswordMetrics;
import android.app.admin.SystemUpdatePolicy;
+import android.app.admin.WifiSsidPolicy;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Intent;
@@ -1112,6 +1117,10 @@ public class DevicePolicyManagerTest extends DpmTestBase {
eq(UserManager.DISALLOW_ADD_MANAGED_PROFILE),
eq(true), eq(UserHandle.SYSTEM));
+ verify(getServices().userManager, times(1)).setUserRestriction(
+ eq(UserManager.DISALLOW_ADD_CLONE_PROFILE),
+ eq(true), eq(UserHandle.SYSTEM));
+
verify(mContext.spiedContext, times(1)).sendBroadcastAsUser(
MockUtils.checkIntentAction(DevicePolicyManager.ACTION_DEVICE_OWNER_CHANGED),
MockUtils.checkUserHandle(UserHandle.USER_SYSTEM));
@@ -1393,6 +1402,10 @@ public class DevicePolicyManagerTest extends DpmTestBase {
eq(false),
MockUtils.checkUserHandle(UserHandle.USER_SYSTEM));
+ verify(getServices().userManager)
+ .setUserRestriction(eq(UserManager.DISALLOW_ADD_CLONE_PROFILE), eq(false),
+ MockUtils.checkUserHandle(UserHandle.USER_SYSTEM));
+
verify(getServices().userManagerInternal).setDevicePolicyUserRestrictions(
eq(UserHandle.USER_SYSTEM), MockUtils.checkUserRestrictions(),
MockUtils.checkUserRestrictions(UserHandle.USER_SYSTEM), eq(true));
@@ -4123,6 +4136,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
ProfileNetworkPreference preferenceDetails2 =
new ProfileNetworkPreference.Builder()
.setPreference(PROFILE_NETWORK_PREFERENCE_ENTERPRISE)
+ .setPreferenceEnterpriseId(NET_ENTERPRISE_ID_1)
.build();
List<ProfileNetworkPreference> preferences2 = new ArrayList<>();
preferences2.add(preferenceDetails);
@@ -7209,8 +7223,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
verify(getServices().alarmManager, times(1)).set(anyInt(), eq(PROFILE_OFF_DEADLINE), any());
// Now the user should see a warning notification.
verify(getServices().notificationManager, times(1))
- .notify(anyInt(), argThat(hasExtra(EXTRA_TITLE, PROFILE_OFF_SUSPENSION_TITLE,
- EXTRA_TEXT, PROFILE_OFF_SUSPENSION_SOON_TEXT)));
+ .notify(anyInt(), any());
// Apps shouldn't be suspended yet.
verifyZeroInteractions(getServices().ipackageManager);
clearInvocations(getServices().alarmManager);
@@ -7224,8 +7237,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
verifyZeroInteractions(getServices().alarmManager);
// Now the user should see a notification about suspended apps.
verify(getServices().notificationManager, times(1))
- .notify(anyInt(), argThat(hasExtra(EXTRA_TITLE, PROFILE_OFF_SUSPENSION_TITLE,
- EXTRA_TEXT, PROFILE_OFF_SUSPENSION_TEXT)));
+ .notify(anyInt(), any());
// Verify that the apps are suspended.
verify(getServices().ipackageManager, times(1)).setPackagesSuspendedAsUser(
any(), eq(true), any(), any(), any(), any(), anyInt());
@@ -7828,6 +7840,128 @@ public class DevicePolicyManagerTest extends DpmTestBase {
() -> dpm.getOrganizationNameForUser(UserHandle.USER_SYSTEM));
}
+ @Test
+ public void testSetWifiMinimumSecurity_noDeviceOwnerOrPoOfOrgOwnedDevice() {
+ assertThrows(SecurityException.class, () -> dpm.setMinimumRequiredWifiSecurityLevel(
+ DevicePolicyManager.WIFI_SECURITY_PERSONAL));
+ }
+
+ @Test
+ public void testSetWifiMinimumSecurity_asDeviceOwner() throws Exception {
+ setDeviceOwner();
+
+ final Set<Integer> allowedLevels = Set.of(WIFI_SECURITY_OPEN, WIFI_SECURITY_PERSONAL,
+ WIFI_SECURITY_ENTERPRISE_EAP, WIFI_SECURITY_ENTERPRISE_192);
+ for (int level : allowedLevels) {
+ dpm.setMinimumRequiredWifiSecurityLevel(level);
+ assertThat(dpm.getMinimumRequiredWifiSecurityLevel()).isEqualTo(level);
+ }
+ }
+
+ @Test
+ public void testSetWifiMinimumSecurity_asPoOfOrgOwnedDevice() throws Exception {
+ final int managedProfileUserId = 15;
+ final int managedProfileAdminUid = UserHandle.getUid(managedProfileUserId, 19436);
+ addManagedProfile(admin1, managedProfileAdminUid, admin1);
+ configureProfileOwnerOfOrgOwnedDevice(admin1, managedProfileUserId);
+ mContext.binder.callingUid = managedProfileAdminUid;
+
+ final Set<Integer> allowedLevels = Set.of(WIFI_SECURITY_OPEN, WIFI_SECURITY_PERSONAL,
+ WIFI_SECURITY_ENTERPRISE_EAP, WIFI_SECURITY_ENTERPRISE_192);
+ for (int level : allowedLevels) {
+ dpm.setMinimumRequiredWifiSecurityLevel(level);
+ assertThat(dpm.getMinimumRequiredWifiSecurityLevel()).isEqualTo(level);
+ }
+ }
+
+ @Test
+ public void testSetSsidAllowlist_noDeviceOwnerOrPoOfOrgOwnedDevice() {
+ final Set<String> ssids = Collections.singleton("ssid1");
+ WifiSsidPolicy policy = WifiSsidPolicy.createAllowlistPolicy(ssids);
+ assertThrows(SecurityException.class, () -> dpm.setWifiSsidPolicy(policy));
+ }
+
+ @Test
+ public void testSetSsidAllowlist_asDeviceOwner() throws Exception {
+ setDeviceOwner();
+
+ final Set<String> ssids = Collections.singleton("ssid1");
+ WifiSsidPolicy policy = WifiSsidPolicy.createAllowlistPolicy(ssids);
+ dpm.setWifiSsidPolicy(policy);
+ assertThat(dpm.getWifiSsidPolicy().getSsids()).isEqualTo(ssids);
+ assertThat(dpm.getWifiSsidPolicy().getPolicyType()).isEqualTo(
+ WifiSsidPolicy.WIFI_SSID_POLICY_TYPE_ALLOWLIST);
+ }
+
+ @Test
+ public void testSetSsidAllowlist_asPoOfOrgOwnedDevice() throws Exception {
+ final int managedProfileUserId = 15;
+ final int managedProfileAdminUid = UserHandle.getUid(managedProfileUserId, 19436);
+ addManagedProfile(admin1, managedProfileAdminUid, admin1);
+ configureProfileOwnerOfOrgOwnedDevice(admin1, managedProfileUserId);
+ mContext.binder.callingUid = managedProfileAdminUid;
+
+ final Set<String> ssids = new ArraySet<>(Arrays.asList("ssid1", "ssid2", "ssid3"));
+ WifiSsidPolicy policy = WifiSsidPolicy.createAllowlistPolicy(ssids);
+ dpm.setWifiSsidPolicy(policy);
+ assertThat(dpm.getWifiSsidPolicy().getSsids()).isEqualTo(ssids);
+ assertThat(dpm.getWifiSsidPolicy().getPolicyType()).isEqualTo(
+ WifiSsidPolicy.WIFI_SSID_POLICY_TYPE_ALLOWLIST);
+ }
+
+ @Test
+ public void testSetSsidAllowlist_emptyList() throws Exception {
+ setDeviceOwner();
+
+ final Set<String> ssids = new ArraySet<>();
+ assertThrows(IllegalArgumentException.class,
+ () -> WifiSsidPolicy.createAllowlistPolicy(ssids));
+ }
+
+ @Test
+ public void testSetSsidDenylist_noDeviceOwnerOrPoOfOrgOwnedDevice() {
+ final Set<String> ssids = Collections.singleton("ssid1");
+ WifiSsidPolicy policy = WifiSsidPolicy.createDenylistPolicy(ssids);
+ assertThrows(SecurityException.class, () -> dpm.setWifiSsidPolicy(policy));
+ }
+
+ @Test
+ public void testSetSsidDenylist_asDeviceOwner() throws Exception {
+ setDeviceOwner();
+
+ final Set<String> ssids = Collections.singleton("ssid1");
+ WifiSsidPolicy policy = WifiSsidPolicy.createDenylistPolicy(ssids);
+ dpm.setWifiSsidPolicy(policy);
+ assertThat(dpm.getWifiSsidPolicy().getSsids()).isEqualTo(ssids);
+ assertThat(dpm.getWifiSsidPolicy().getPolicyType()).isEqualTo(
+ WifiSsidPolicy.WIFI_SSID_POLICY_TYPE_DENYLIST);
+ }
+
+ @Test
+ public void testSetSsidDenylist_asPoOfOrgOwnedDevice() throws Exception {
+ final int managedProfileUserId = 15;
+ final int managedProfileAdminUid = UserHandle.getUid(managedProfileUserId, 19436);
+ addManagedProfile(admin1, managedProfileAdminUid, admin1);
+ configureProfileOwnerOfOrgOwnedDevice(admin1, managedProfileUserId);
+ mContext.binder.callingUid = managedProfileAdminUid;
+
+ final Set<String> ssids = new ArraySet<>(Arrays.asList("ssid1", "ssid2", "ssid3"));
+ WifiSsidPolicy policy = WifiSsidPolicy.createDenylistPolicy(ssids);
+ dpm.setWifiSsidPolicy(policy);
+ assertThat(dpm.getWifiSsidPolicy().getSsids()).isEqualTo(ssids);
+ assertThat(dpm.getWifiSsidPolicy().getPolicyType()).isEqualTo(
+ WifiSsidPolicy.WIFI_SSID_POLICY_TYPE_DENYLIST);
+ }
+
+ @Test
+ public void testSetSsidDenylist_emptyList() throws Exception {
+ setDeviceOwner();
+
+ final Set<String> ssids = new ArraySet<>();
+ assertThrows(IllegalArgumentException.class,
+ () -> WifiSsidPolicy.createDenylistPolicy(ssids));
+ }
+
private void setupVpnAuthorization(String userVpnPackage, int userVpnUid) {
final AppOpsManager.PackageOps vpnOp = new AppOpsManager.PackageOps(userVpnPackage,
userVpnUid, List.of(new AppOpsManager.OpEntry(
@@ -7863,18 +7997,6 @@ public class DevicePolicyManagerTest extends DpmTestBase {
// To allow creation of Notification via Notification.Builder
mContext.applicationInfo = mRealTestContext.getApplicationInfo();
- // Setup resources to render notification titles and texts.
- when(mServiceContext.resources
- .getString(R.string.personal_apps_suspension_title))
- .thenReturn(PROFILE_OFF_SUSPENSION_TITLE);
- when(mServiceContext.resources
- .getString(R.string.personal_apps_suspension_text))
- .thenReturn(PROFILE_OFF_SUSPENSION_TEXT);
- when(mServiceContext.resources
- .getString(eq(R.string.personal_apps_suspension_soon_text),
- anyString(), anyString(), anyInt()))
- .thenReturn(PROFILE_OFF_SUSPENSION_SOON_TEXT);
-
// Make locale available for date formatting:
when(mServiceContext.resources.getConfiguration())
.thenReturn(mRealTestContext.getResources().getConfiguration());
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
index d4b1165f6a08..6eb2085d24bb 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -232,6 +232,8 @@ public class DpmMockContext extends MockContext {
return mMockSystemServices.crossProfileApps;
case Context.VPN_MANAGEMENT_SERVICE:
return mMockSystemServices.vpnManager;
+ case Context.DEVICE_POLICY_SERVICE:
+ return mMockSystemServices.devicePolicyManager;
}
throw new UnsupportedOperationException();
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
index 8a2919d55216..597a165cbb0f 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
@@ -31,6 +31,7 @@ import android.app.AppOpsManager;
import android.app.IActivityManager;
import android.app.IActivityTaskManager;
import android.app.NotificationManager;
+import android.app.admin.DevicePolicyManager;
import android.app.backup.IBackupManager;
import android.app.usage.UsageStatsManagerInternal;
import android.content.BroadcastReceiver;
@@ -125,6 +126,7 @@ public class MockSystemServices {
public final AppOpsManager appOpsManager;
public final UsbManager usbManager;
public final VpnManager vpnManager;
+ public final DevicePolicyManager devicePolicyManager;
/** Note this is a partial mock, not a real mock. */
public final PackageManager packageManager;
public final BuildMock buildMock = new BuildMock();
@@ -172,6 +174,7 @@ public class MockSystemServices {
appOpsManager = mock(AppOpsManager.class);
usbManager = mock(UsbManager.class);
vpnManager = mock(VpnManager.class);
+ devicePolicyManager = mock(DevicePolicyManager.class);
// Package manager is huge, so we use a partial mock instead.
packageManager = spy(realContext.getPackageManager());
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index 9a6f61e7c6cf..81c98717d2e7 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -1746,7 +1746,7 @@ public class NetworkPolicyManagerServiceTest {
}
private void triggerOnStatsProviderWarningOrLimitReached() throws InterruptedException {
- mService.onStatsProviderWarningOrLimitReached();
+ mService.notifyStatsProviderWarningOrLimitReached();
// Wait for processing of MSG_STATS_PROVIDER_WARNING_OR_LIMIT_REACHED.
postMsgAndWaitForCompletion();
verify(mStatsManager).forceUpdate();
@@ -2046,7 +2046,7 @@ public class NetworkPolicyManagerServiceTest {
private static NetworkStateSnapshot buildWifi() {
WifiInfo mockWifiInfo = mock(WifiInfo.class);
when(mockWifiInfo.makeCopy(anyLong())).thenReturn(mockWifiInfo);
- when(mockWifiInfo.getCurrentNetworkKey()).thenReturn(TEST_WIFI_NETWORK_KEY);
+ when(mockWifiInfo.getNetworkKey()).thenReturn(TEST_WIFI_NETWORK_KEY);
final LinkProperties prop = new LinkProperties();
prop.setInterfaceName(TEST_IFACE);
final NetworkCapabilities networkCapabilities = new NetworkCapabilities.Builder()
@@ -2092,7 +2092,7 @@ public class NetworkPolicyManagerServiceTest {
}
private void verifyAdvisePersistThreshold() throws Exception {
- verify(mStatsManager).advisePersistThreshold(anyLong());
+ verify(mStatsManager).setDefaultGlobalAlert(anyLong());
}
private static class TestAbstractFuture<T> extends AbstractFuture<T> {
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
index 408d2c525f70..99edecfeed30 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
@@ -16,6 +16,7 @@
package com.android.server.pm;
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertBundlesEqual;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertEmpty;
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertExpectException;
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertWith;
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.list;
@@ -257,6 +258,10 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
.setLongLived(true)
.setExtras(pb)
.setStartingTheme(android.R.style.Theme_Black_NoTitleBar_Fullscreen)
+ .addCapabilityBinding("action.intent.START_EXERCISE",
+ "exercise.type", list("running", "jogging"))
+ .addCapabilityBinding("action.intent.START_EXERCISE",
+ "exercise.duration", list("10m"))
.build();
si.addFlags(ShortcutInfo.FLAG_PINNED);
si.setBitmapPath("abc");
@@ -294,6 +299,13 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
assertEquals(null, si.getDisabledMessageResName());
assertEquals("android:style/Theme.Black.NoTitleBar.Fullscreen",
si.getStartingThemeResName());
+ assertTrue(si.hasCapability("action.intent.START_EXERCISE"));
+ assertFalse(si.hasCapability(""));
+ assertFalse(si.hasCapability("random"));
+ assertEquals(list("running", "jogging"), si.getCapabilityParameterValues(
+ "action.intent.START_EXERCISE", "exercise.type"));
+ assertEmpty(si.getCapabilityParameterValues("action.intent.START_EXERCISE", ""));
+ assertEmpty(si.getCapabilityParameterValues("action.intent.START_EXERCISE", "random"));
}
public void testShortcutInfoParcel_resId() {
@@ -947,6 +959,10 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
.setRank(123)
.setExtras(pb)
.setLocusId(new LocusId("1.2.3.4.5"))
+ .addCapabilityBinding("action.intent.START_EXERCISE",
+ "exercise.type", list("running", "jogging"))
+ .addCapabilityBinding("action.intent.START_EXERCISE",
+ "exercise.duration", list("10m"))
.build();
sorig.setTimestamp(mInjectedCurrentTimeMillis);
@@ -1008,6 +1024,14 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
assertNull(si.getIconUri());
assertTrue(si.getLastChangedTimestamp() < now);
+ assertTrue(si.hasCapability("action.intent.START_EXERCISE"));
+ assertFalse(si.hasCapability(""));
+ assertFalse(si.hasCapability("random"));
+ assertEquals(list("running", "jogging"), si.getCapabilityParameterValues(
+ "action.intent.START_EXERCISE", "exercise.type"));
+ assertEmpty(si.getCapabilityParameterValues("action.intent.START_EXERCISE", ""));
+ assertEmpty(si.getCapabilityParameterValues("action.intent.START_EXERCISE", "random"));
+
// Make sure ranks are saved too. Because of the auto-adjusting, we need two shortcuts
// to test it.
si = mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "id2", USER_10);
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index 429445f80dbb..7e5fe0496a1c 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -664,6 +664,24 @@ public final class UserManagerTest {
}
}
+ // Make sure createProfile would fail if we have DISALLOW_ADD_CLONE_PROFILE.
+ @MediumTest
+ @Test
+ public void testCreateUser_disallowAddClonedUserProfile() throws Exception {
+ final int primaryUserId = ActivityManager.getCurrentUser();
+ final UserHandle primaryUserHandle = asHandle(primaryUserId);
+ mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_CLONE_PROFILE,
+ true, primaryUserHandle);
+ try {
+ UserInfo cloneProfileUserInfo = createProfileForUser("Clone",
+ UserManager.USER_TYPE_PROFILE_CLONE, primaryUserId);
+ assertThat(cloneProfileUserInfo).isNull();
+ } finally {
+ mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_CLONE_PROFILE, false,
+ primaryUserHandle);
+ }
+ }
+
// Make sure createProfile would fail if we have DISALLOW_ADD_MANAGED_PROFILE.
@MediumTest
@Test
diff --git a/services/tests/servicestests/src/com/android/server/statusbar/StatusBarManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/statusbar/StatusBarManagerServiceTest.java
index d164d2a4f581..0e98b5e79aa0 100644
--- a/services/tests/servicestests/src/com/android/server/statusbar/StatusBarManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/statusbar/StatusBarManagerServiceTest.java
@@ -29,6 +29,7 @@ import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.argThat;
import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -38,6 +39,7 @@ import android.app.ActivityManagerInternal;
import android.app.StatusBarManager;
import android.content.ComponentName;
import android.content.Intent;
+import android.content.om.IOverlayManager;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
@@ -96,6 +98,10 @@ public class StatusBarManagerServiceTest {
private ApplicationInfo mApplicationInfo;
@Mock
private IStatusBar.Stub mMockStatusBar;
+ @Mock
+ private IOverlayManager mOverlayManager;
+ @Mock
+ private PackageManager mPackageManager;
@Captor
private ArgumentCaptor<IAddTileResultCallback> mAddTileResultCallbackCaptor;
@@ -130,6 +136,7 @@ public class StatusBarManagerServiceTest {
mStatusBarManagerService);
mStatusBarManagerService.registerStatusBar(mMockStatusBar);
+ mStatusBarManagerService.registerOverlayManager(mOverlayManager);
mIcon = Icon.createWithResource(mContext, android.R.drawable.btn_plus);
}
@@ -577,27 +584,56 @@ public class StatusBarManagerServiceTest {
}
@Test
- public void testSetNavBarModeOverride_setsOverrideModeKids() {
+ public void testSetNavBarModeOverride_setsOverrideModeKids() throws RemoteException {
int navBarModeOverrideKids = StatusBarManager.NAV_BAR_MODE_OVERRIDE_KIDS;
+
mStatusBarManagerService.setNavBarModeOverride(navBarModeOverrideKids);
assertEquals(navBarModeOverrideKids, mStatusBarManagerService.getNavBarModeOverride());
+ verify(mOverlayManager).setEnabledExclusiveInCategory(anyString(), anyInt());
}
@Test
- public void testSetNavBarModeOverride_setsOverrideModeNone() {
+ public void testSetNavBarModeOverride_setsOverrideModeNone() throws RemoteException {
int navBarModeOverrideNone = StatusBarManager.NAV_BAR_MODE_OVERRIDE_NONE;
+
mStatusBarManagerService.setNavBarModeOverride(navBarModeOverrideNone);
assertEquals(navBarModeOverrideNone, mStatusBarManagerService.getNavBarModeOverride());
+ verify(mOverlayManager, never()).setEnabledExclusiveInCategory(anyString(), anyInt());
}
@Test
- public void testSetNavBarModeOverride_invalidInputThrowsError() {
+ public void testSetNavBarModeOverride_invalidInputThrowsError() throws RemoteException {
int navBarModeOverrideInvalid = -1;
assertThrows(UnsupportedOperationException.class,
() -> mStatusBarManagerService.setNavBarModeOverride(navBarModeOverrideInvalid));
+ verify(mOverlayManager, never()).setEnabledExclusiveInCategory(anyString(), anyInt());
+ }
+
+ @Test
+ public void testSetNavBarModeOverride_noOverlayManagerDoesNotEnable() throws RemoteException {
+ mOverlayManager = null;
+ int navBarModeOverrideKids = StatusBarManager.NAV_BAR_MODE_OVERRIDE_KIDS;
+
+ mStatusBarManagerService.setNavBarModeOverride(navBarModeOverrideKids);
+
+ assertEquals(navBarModeOverrideKids, mStatusBarManagerService.getNavBarModeOverride());
+ verify(mOverlayManager, never()).setEnabledExclusiveInCategory(anyString(), anyInt());
+ }
+
+ @Test
+ public void testSetNavBarModeOverride_noPackageDoesNotEnable() throws Exception {
+ mContext.setMockPackageManager(mPackageManager);
+ when(mPackageManager.getPackageInfo(anyString(),
+ any(PackageManager.PackageInfoFlags.class))).thenReturn(null);
+ int navBarModeOverrideKids = StatusBarManager.NAV_BAR_MODE_OVERRIDE_KIDS;
+
+ mStatusBarManagerService.setNavBarModeOverride(navBarModeOverrideKids);
+
+ assertEquals(navBarModeOverrideKids, mStatusBarManagerService.getNavBarModeOverride());
+ verify(mOverlayManager, never()).setEnabledExclusiveInCategory(anyString(), anyInt());
}
private void mockUidCheck() {
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java b/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
index 88f368e403a6..06726b031a36 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
@@ -22,16 +22,20 @@ import static android.app.usage.UsageStatsManager.REASON_MAIN_TIMEOUT;
import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE;
import static android.app.usage.UsageStatsManager.REASON_SUB_FORCED_SYSTEM_FLAG_BACKGROUND_RESOURCE_USAGE;
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_FOREGROUND;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_NOTIFICATION_SEEN;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SLICE_PINNED;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RESTRICTED;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
+import static android.app.usage.UsageStatsManager.standbyBucketToString;
import android.os.FileUtils;
import android.test.AndroidTestCase;
import java.io.File;
+import java.util.Map;
public class AppIdleHistoryTests extends AndroidTestCase {
@@ -65,7 +69,7 @@ public class AppIdleHistoryTests extends AndroidTestCase {
// Screen On time file should be written right away
assertTrue(aih.getScreenOnTimeFile().exists());
- aih.writeAppIdleTimes(USER_ID);
+ aih.writeAppIdleTimes(USER_ID, /* elapsedRealtime= */ 2000);
// stats file should be written now
assertTrue(new File(new File(mStorageDir, "users/" + USER_ID),
AppIdleHistory.APP_IDLE_FILENAME).exists());
@@ -128,7 +132,7 @@ public class AppIdleHistoryTests extends AndroidTestCase {
// Check persistence
aih.writeAppIdleDurations();
- aih.writeAppIdleTimes(USER_ID);
+ aih.writeAppIdleTimes(USER_ID, /* elapsedRealtime= */ 3000);
aih = new AppIdleHistory(mStorageDir, 4000);
assertEquals(aih.getAppStandbyBucket(PACKAGE_1, USER_ID, 5000), STANDBY_BUCKET_RARE);
assertEquals(aih.getAppStandbyBucket(PACKAGE_2, USER_ID, 5000), STANDBY_BUCKET_ACTIVE);
@@ -165,7 +169,7 @@ public class AppIdleHistoryTests extends AndroidTestCase {
aih.getAppStandbyReason(PACKAGE_1, USER_ID, 3000));
aih.setAppStandbyBucket(PACKAGE_1, USER_ID, 4000, STANDBY_BUCKET_WORKING_SET,
REASON_MAIN_TIMEOUT);
- aih.writeAppIdleTimes(USER_ID);
+ aih.writeAppIdleTimes(USER_ID, /* elapsedRealtime= */ 4000);
aih = new AppIdleHistory(mStorageDir, 5000);
assertEquals(REASON_MAIN_TIMEOUT, aih.getAppStandbyReason(PACKAGE_1, USER_ID, 5000));
@@ -180,11 +184,63 @@ public class AppIdleHistoryTests extends AndroidTestCase {
aih.reportUsage(null, USER_ID, STANDBY_BUCKET_ACTIVE,
REASON_SUB_USAGE_MOVE_TO_FOREGROUND, 2000, 0);
// Persist data
- aih.writeAppIdleTimes(USER_ID);
+ aih.writeAppIdleTimes(USER_ID, /* elapsedRealtime= */ 2000);
// Recover data from disk
aih = new AppIdleHistory(mStorageDir, 5000);
// Verify data is intact
assertEquals(REASON_MAIN_USAGE | REASON_SUB_USAGE_MOVE_TO_FOREGROUND,
aih.getAppStandbyReason(PACKAGE_1, USER_ID, 3000));
}
+
+ public void testBucketExpiryTimes() throws Exception {
+ AppIdleHistory aih = new AppIdleHistory(mStorageDir, 1000 /* elapsedRealtime */);
+ aih.reportUsage(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
+ REASON_SUB_USAGE_SLICE_PINNED,
+ 2000 /* elapsedRealtime */, 6000 /* expiryRealtime */);
+ assertEquals(5000 /* expectedExpiryTimeMs */, aih.getBucketExpiryTimeMs(PACKAGE_1, USER_ID,
+ STANDBY_BUCKET_WORKING_SET, 2000 /* elapsedRealtime */));
+ aih.reportUsage(PACKAGE_2, USER_ID, STANDBY_BUCKET_FREQUENT,
+ REASON_SUB_USAGE_NOTIFICATION_SEEN,
+ 2000 /* elapsedRealtime */, 3000 /* expiryRealtime */);
+ assertEquals(2000 /* expectedExpiryTimeMs */, aih.getBucketExpiryTimeMs(PACKAGE_2, USER_ID,
+ STANDBY_BUCKET_FREQUENT, 2000 /* elapsedRealtime */));
+ aih.writeAppIdleTimes(USER_ID, 4000 /* elapsedRealtime */);
+
+ // Persist data
+ aih = new AppIdleHistory(mStorageDir, 5000 /* elapsedRealtime */);
+ final Map<Integer, Long> expectedExpiryTimes1 = Map.of(
+ STANDBY_BUCKET_ACTIVE, 0L,
+ STANDBY_BUCKET_WORKING_SET, 5000L,
+ STANDBY_BUCKET_FREQUENT, 0L,
+ STANDBY_BUCKET_RARE, 0L,
+ STANDBY_BUCKET_RESTRICTED, 0L
+ );
+ // For PACKAGE_1, only WORKING_SET bucket should have an expiry time.
+ verifyBucketExpiryTimes(aih, PACKAGE_1, USER_ID, 5000 /* elapsedRealtime */,
+ expectedExpiryTimes1);
+ final Map<Integer, Long> expectedExpiryTimes2 = Map.of(
+ STANDBY_BUCKET_ACTIVE, 0L,
+ STANDBY_BUCKET_WORKING_SET, 0L,
+ STANDBY_BUCKET_FREQUENT, 0L,
+ STANDBY_BUCKET_RARE, 0L,
+ STANDBY_BUCKET_RESTRICTED, 0L
+ );
+ // For PACKAGE_2, there shouldn't be any expiry time since the one set earlier would have
+ // elapsed by the time the data was persisted to disk
+ verifyBucketExpiryTimes(aih, PACKAGE_2, USER_ID, 5000 /* elapsedRealtime */,
+ expectedExpiryTimes2);
+ }
+
+ private void verifyBucketExpiryTimes(AppIdleHistory aih, String packageName, int userId,
+ long elapsedRealtimeMs, Map<Integer, Long> expectedExpiryTimesMs) throws Exception {
+ for (Map.Entry<Integer, Long> entry : expectedExpiryTimesMs.entrySet()) {
+ final int bucket = entry.getKey();
+ final long expectedExpiryTimeMs = entry.getValue();
+ final long actualExpiryTimeMs = aih.getBucketExpiryTimeMs(packageName, userId, bucket,
+ elapsedRealtimeMs);
+ assertEquals("Unexpected expiry time for pkg=" + packageName + ", userId=" + userId
+ + ", bucket=" + standbyBucketToString(bucket),
+ expectedExpiryTimeMs, actualExpiryTimeMs);
+ }
+ }
} \ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index 949ee01d6872..18d3f3d0e805 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -63,7 +63,6 @@ import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyLong;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
@@ -828,7 +827,6 @@ public class AppStandbyControllerTests {
}
@Test
- @FlakyTest(bugId = 185169504)
public void testNotificationEvent() throws Exception {
reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_1));
@@ -842,6 +840,22 @@ public class AppStandbyControllerTests {
}
@Test
+ public void testNotificationEvent_changePromotedBucket() throws Exception {
+ mController.forceIdleState(PACKAGE_1, USER_ID, true);
+ reportEvent(mController, NOTIFICATION_SEEN, mInjector.mElapsedRealtime, PACKAGE_1);
+ assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(mController, PACKAGE_1));
+
+ // TODO: Avoid hardcoding these string constants.
+ mInjector.mSettingsBuilder.setInt("notification_seen_promoted_bucket",
+ STANDBY_BUCKET_FREQUENT);
+ mInjector.mPropertiesChangedListener.onPropertiesChanged(
+ mInjector.getDeviceConfigProperties());
+ mController.forceIdleState(PACKAGE_1, USER_ID, true);
+ reportEvent(mController, NOTIFICATION_SEEN, mInjector.mElapsedRealtime, PACKAGE_1);
+ assertEquals(STANDBY_BUCKET_FREQUENT, getStandbyBucket(mController, PACKAGE_1));
+ }
+
+ @Test
@FlakyTest(bugId = 185169504)
public void testSlicePinnedEvent() throws Exception {
reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/DeviceVibrationEffectAdapterTest.java b/services/tests/servicestests/src/com/android/server/vibrator/DeviceVibrationEffectAdapterTest.java
index 739b3b179de7..5e9e16a0baf8 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/DeviceVibrationEffectAdapterTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/DeviceVibrationEffectAdapterTest.java
@@ -53,10 +53,10 @@ public class DeviceVibrationEffectAdapterTest {
private static final float[] TEST_AMPLITUDE_MAP = new float[]{
/* 50Hz= */ 0.1f, 0.2f, 0.4f, 0.8f, /* 150Hz= */ 1f, 0.9f, /* 200Hz= */ 0.8f};
- private static final VibratorInfo.FrequencyMapping EMPTY_FREQUENCY_MAPPING =
- new VibratorInfo.FrequencyMapping(Float.NaN, Float.NaN, Float.NaN, null);
- private static final VibratorInfo.FrequencyMapping TEST_FREQUENCY_MAPPING =
- new VibratorInfo.FrequencyMapping(TEST_RESONANT_FREQUENCY, TEST_MIN_FREQUENCY,
+ private static final VibratorInfo.FrequencyProfile EMPTY_FREQUENCY_PROFILE =
+ new VibratorInfo.FrequencyProfile(Float.NaN, Float.NaN, Float.NaN, null);
+ private static final VibratorInfo.FrequencyProfile TEST_FREQUENCY_PROFILE =
+ new VibratorInfo.FrequencyProfile(TEST_RESONANT_FREQUENCY, TEST_MIN_FREQUENCY,
TEST_FREQUENCY_RESOLUTION, TEST_AMPLITUDE_MAP);
private DeviceVibrationEffectAdapter mAdapter;
@@ -79,8 +79,8 @@ public class DeviceVibrationEffectAdapterTest {
new PrimitiveSegment(VibrationEffect.Composition.PRIMITIVE_SPIN, 0.5f, 100)),
/* repeatIndex= */ -1);
- assertEquals(effect, mAdapter.apply(effect, createVibratorInfo(EMPTY_FREQUENCY_MAPPING)));
- assertEquals(effect, mAdapter.apply(effect, createVibratorInfo(TEST_FREQUENCY_MAPPING)));
+ assertEquals(effect, mAdapter.apply(effect, createVibratorInfo(EMPTY_FREQUENCY_PROFILE)));
+ assertEquals(effect, mAdapter.apply(effect, createVibratorInfo(TEST_FREQUENCY_PROFILE)));
}
@Test
@@ -97,7 +97,7 @@ public class DeviceVibrationEffectAdapterTest {
/* repeatIndex= */ 3);
VibrationEffect.Composed adaptedEffect = (VibrationEffect.Composed) mAdapter.apply(effect,
- createVibratorInfo(EMPTY_FREQUENCY_MAPPING));
+ createVibratorInfo(EMPTY_FREQUENCY_PROFILE));
assertTrue(adaptedEffect.getSegments().size() > effect.getSegments().size());
assertTrue(adaptedEffect.getRepeatIndex() >= effect.getRepeatIndex());
@@ -128,7 +128,7 @@ public class DeviceVibrationEffectAdapterTest {
/* startFrequencyHz= */ 200, /* endFrequencyHz= */ 50, /* duration= */ 20)),
/* repeatIndex= */ 2);
- VibratorInfo info = createVibratorInfo(TEST_FREQUENCY_MAPPING,
+ VibratorInfo info = createVibratorInfo(TEST_FREQUENCY_PROFILE,
IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
assertEquals(expected, mAdapter.apply(effect, info));
}
@@ -159,7 +159,7 @@ public class DeviceVibrationEffectAdapterTest {
/* duration= */ 20)),
/* repeatIndex= */ 2);
- VibratorInfo info = createVibratorInfo(EMPTY_FREQUENCY_MAPPING,
+ VibratorInfo info = createVibratorInfo(EMPTY_FREQUENCY_PROFILE,
IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
assertEquals(expected, mAdapter.apply(effect, info));
}
@@ -188,17 +188,17 @@ public class DeviceVibrationEffectAdapterTest {
/* startFrequencyHz= */ 200, /* endFrequencyHz= */ 50, /* duration= */ 20)),
/* repeatIndex= */ 2);
- VibratorInfo info = createVibratorInfo(TEST_FREQUENCY_MAPPING,
+ VibratorInfo info = createVibratorInfo(TEST_FREQUENCY_PROFILE,
IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
assertEquals(expected, mAdapter.apply(effect, info));
}
- private static VibratorInfo createVibratorInfo(VibratorInfo.FrequencyMapping frequencyMapping,
+ private static VibratorInfo createVibratorInfo(VibratorInfo.FrequencyProfile frequencyProfile,
int... capabilities) {
int cap = IntStream.of(capabilities).reduce((a, b) -> a | b).orElse(0);
return new VibratorInfo.Builder(0)
.setCapabilities(cap)
- .setFrequencyMapping(frequencyMapping)
+ .setFrequencyProfile(frequencyProfile)
.build();
}
}
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java b/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java
index 2ad0e93dd1fb..e88e9881181c 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java
@@ -170,7 +170,7 @@ final class FakeVibratorControllerProvider {
}
infoBuilder.setCompositionSizeMax(mCompositionSizeMax);
infoBuilder.setQFactor(mQFactor);
- infoBuilder.setFrequencyMapping(new VibratorInfo.FrequencyMapping(
+ infoBuilder.setFrequencyProfile(new VibratorInfo.FrequencyProfile(
mResonantFrequency, mMinFrequency, mFrequencyResolution, mMaxAmplitudes));
return mIsInfoLoadSuccessful;
}
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/RampToStepAdapterTest.java b/services/tests/servicestests/src/com/android/server/vibrator/RampToStepAdapterTest.java
index 22db91736756..a9f37f35e35b 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/RampToStepAdapterTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/RampToStepAdapterTest.java
@@ -47,8 +47,8 @@ public class RampToStepAdapterTest {
private static final int TEST_STEP_DURATION = 5;
private static final float[] TEST_AMPLITUDE_MAP = new float[]{
/* 50Hz= */ 0.1f, 0.2f, 0.4f, 0.8f, /* 150Hz= */ 1f, 0.9f, /* 200Hz= */ 0.8f};
- private static final VibratorInfo.FrequencyMapping TEST_FREQUENCY_MAPPING =
- new VibratorInfo.FrequencyMapping(
+ private static final VibratorInfo.FrequencyProfile TEST_FREQUENCY_PROFILE =
+ new VibratorInfo.FrequencyProfile(
/* resonantFrequencyHz= */ 150f, /* minFrequencyHz= */ 50f,
/* frequencyResolutionHz= */ 25f, TEST_AMPLITUDE_MAP);
@@ -124,7 +124,7 @@ public class RampToStepAdapterTest {
private static VibratorInfo createVibratorInfo(int... capabilities) {
return new VibratorInfo.Builder(0)
.setCapabilities(IntStream.of(capabilities).reduce((a, b) -> a | b).orElse(0))
- .setFrequencyMapping(TEST_FREQUENCY_MAPPING)
+ .setFrequencyProfile(TEST_FREQUENCY_PROFILE)
.build();
}
}
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/StepToRampAdapterTest.java b/services/tests/servicestests/src/com/android/server/vibrator/StepToRampAdapterTest.java
index 18ff953446a2..54627c40c7e0 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/StepToRampAdapterTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/StepToRampAdapterTest.java
@@ -46,8 +46,8 @@ import java.util.stream.IntStream;
public class StepToRampAdapterTest {
private static final float[] TEST_AMPLITUDE_MAP = new float[]{
/* 50Hz= */ 0.1f, 0.2f, 0.4f, 0.8f, /* 150Hz= */ 1f, 0.9f, /* 200Hz= */ 0.8f};
- private static final VibratorInfo.FrequencyMapping TEST_FREQUENCY_MAPPING =
- new VibratorInfo.FrequencyMapping(
+ private static final VibratorInfo.FrequencyProfile TEST_FREQUENCY_PROFILE =
+ new VibratorInfo.FrequencyProfile(
/* resonantFrequencyHz= */ 150f, /* minFrequencyHz= */ 50f,
/* frequencyResolutionHz= */ 25f, TEST_AMPLITUDE_MAP);
@@ -99,7 +99,7 @@ public class StepToRampAdapterTest {
VibratorInfo vibratorInfo = new VibratorInfo.Builder(0)
.setCapabilities(IVibrator.CAP_COMPOSE_PWLE_EFFECTS)
.setPwlePrimitiveDurationMax(10)
- .setFrequencyMapping(TEST_FREQUENCY_MAPPING)
+ .setFrequencyProfile(TEST_FREQUENCY_PROFILE)
.build();
// Update repeat index to skip the ramp splits.
@@ -191,7 +191,7 @@ public class StepToRampAdapterTest {
private static VibratorInfo createVibratorInfo(int... capabilities) {
return new VibratorInfo.Builder(0)
.setCapabilities(IntStream.of(capabilities).reduce((a, b) -> a | b).orElse(0))
- .setFrequencyMapping(TEST_FREQUENCY_MAPPING)
+ .setFrequencyProfile(TEST_FREQUENCY_PROFILE)
.build();
}
}
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationScalerTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationScalerTest.java
index 6369dbc6b171..81677101c139 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationScalerTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationScalerTest.java
@@ -133,13 +133,14 @@ public class VibrationScalerTest {
mVibrationScaler.getExternalVibrationScale(USAGE_TOUCH));
setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
- // Unexpected vibration intensity will be treated as SCALE_NONE.
+ // Vibration setting being bypassed will use default setting and not scale.
assertEquals(IExternalVibratorService.SCALE_NONE,
mVibrationScaler.getExternalVibrationScale(USAGE_TOUCH));
}
@Test
public void scale_withPrebakedSegment_setsEffectStrengthBasedOnSettings() {
+ setDefaultIntensity(USAGE_NOTIFICATION, VIBRATION_INTENSITY_MEDIUM);
setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_HIGH);
PrebakedSegment effect = new PrebakedSegment(VibrationEffect.EFFECT_CLICK,
/* shouldFallback= */ false, VibrationEffect.EFFECT_STRENGTH_MEDIUM);
@@ -158,35 +159,33 @@ public class VibrationScalerTest {
setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF);
scaled = mVibrationScaler.scale(effect, USAGE_NOTIFICATION);
- // Unexpected intensity setting will be mapped to STRONG.
- assertEquals(scaled.getEffectStrength(), VibrationEffect.EFFECT_STRENGTH_STRONG);
+ // Vibration setting being bypassed will use default setting.
+ assertEquals(scaled.getEffectStrength(), VibrationEffect.EFFECT_STRENGTH_MEDIUM);
}
@Test
public void scale_withPrebakedEffect_setsEffectStrengthBasedOnSettings() {
+ setDefaultIntensity(USAGE_NOTIFICATION, VIBRATION_INTENSITY_LOW);
setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_HIGH);
VibrationEffect effect = VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK);
- PrebakedSegment scaled = getFirstSegment(mVibrationScaler.scale(
- effect, USAGE_NOTIFICATION));
+ PrebakedSegment scaled =
+ getFirstSegment(mVibrationScaler.scale(effect, USAGE_NOTIFICATION));
assertEquals(scaled.getEffectStrength(), VibrationEffect.EFFECT_STRENGTH_STRONG);
setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
VIBRATION_INTENSITY_MEDIUM);
- scaled = getFirstSegment(mVibrationScaler.scale(
- effect, USAGE_NOTIFICATION));
+ scaled = getFirstSegment(mVibrationScaler.scale(effect, USAGE_NOTIFICATION));
assertEquals(scaled.getEffectStrength(), VibrationEffect.EFFECT_STRENGTH_MEDIUM);
setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_LOW);
- scaled = getFirstSegment(mVibrationScaler.scale(
- effect, USAGE_NOTIFICATION));
+ scaled = getFirstSegment(mVibrationScaler.scale(effect, USAGE_NOTIFICATION));
assertEquals(scaled.getEffectStrength(), VibrationEffect.EFFECT_STRENGTH_LIGHT);
setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF);
- scaled = getFirstSegment(mVibrationScaler.scale(
- effect, USAGE_NOTIFICATION));
- // Unexpected intensity setting will be mapped to STRONG.
- assertEquals(scaled.getEffectStrength(), VibrationEffect.EFFECT_STRENGTH_STRONG);
+ scaled = getFirstSegment(mVibrationScaler.scale(effect, USAGE_NOTIFICATION));
+ // Vibration setting being bypassed will use default setting.
+ assertEquals(scaled.getEffectStrength(), VibrationEffect.EFFECT_STRENGTH_LIGHT);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
index ff59d0f22c3c..5d4ffbb6aca2 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
@@ -319,6 +319,34 @@ public class VibrationSettingsTest {
}
}
+
+ @Test
+ public void shouldIgnoreVibration_vibrateOnDisabled_ignoresUsagesNotAccessibility() {
+ setUserSetting(Settings.System.VIBRATE_ON, 0);
+
+ for (int usage : ALL_USAGES) {
+ if (usage == USAGE_ACCESSIBILITY) {
+ assertVibrationNotIgnoredForUsage(usage);
+ } else {
+ assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_FOR_SETTINGS);
+ }
+ assertVibrationNotIgnoredForUsageAndFlags(usage,
+ VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF);
+ }
+ }
+
+ @Test
+ public void shouldIgnoreVibration_vibrateOnEnabledOrUnset_allowsAnyUsage() {
+ deleteUserSetting(Settings.System.VIBRATE_ON);
+ for (int usage : ALL_USAGES) {
+ assertVibrationNotIgnoredForUsage(usage);
+ }
+
+ setUserSetting(Settings.System.VIBRATE_ON, 1);
+ for (int usage : ALL_USAGES) {
+ assertVibrationNotIgnoredForUsage(usage);
+ }
+ }
@Test
public void shouldIgnoreVibration_withRingSettingsOff_disableRingtoneVibrations() {
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
@@ -330,6 +358,8 @@ public class VibrationSettingsTest {
} else {
assertVibrationNotIgnoredForUsage(usage);
}
+ assertVibrationNotIgnoredForUsageAndFlags(usage,
+ VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF);
}
}
@@ -363,6 +393,8 @@ public class VibrationSettingsTest {
} else {
assertVibrationNotIgnoredForUsage(usage);
}
+ assertVibrationNotIgnoredForUsageAndFlags(usage,
+ VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF);
}
}
@@ -376,6 +408,8 @@ public class VibrationSettingsTest {
} else {
assertVibrationNotIgnoredForUsage(usage);
}
+ assertVibrationNotIgnoredForUsageAndFlags(usage,
+ VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF);
}
}
@@ -389,6 +423,8 @@ public class VibrationSettingsTest {
} else {
assertVibrationNotIgnoredForUsage(usage);
}
+ assertVibrationNotIgnoredForUsageAndFlags(usage,
+ VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF);
}
}
@@ -402,6 +438,8 @@ public class VibrationSettingsTest {
} else {
assertVibrationNotIgnoredForUsage(usage);
}
+ assertVibrationNotIgnoredForUsageAndFlags(usage,
+ VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF);
}
}
@@ -419,6 +457,8 @@ public class VibrationSettingsTest {
} else {
assertVibrationNotIgnoredForUsage(usage);
}
+ assertVibrationNotIgnoredForUsageAndFlags(usage,
+ VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF);
}
}
@@ -522,9 +562,17 @@ public class VibrationSettingsTest {
}
private void assertVibrationNotIgnoredForUsage(@VibrationAttributes.Usage int usage) {
+ assertVibrationNotIgnoredForUsageAndFlags(usage, /* flags= */ 0);
+ }
+
+ private void assertVibrationNotIgnoredForUsageAndFlags(@VibrationAttributes.Usage int usage,
+ @VibrationAttributes.Flag int flags) {
assertNull(errorMessageForUsage(usage),
mVibrationSettings.shouldIgnoreVibration(UID,
- VibrationAttributes.createForUsage(usage)));
+ new VibrationAttributes.Builder()
+ .setUsage(usage)
+ .setFlags(flags, VibrationAttributes.FLAG_ALL_SUPPORTED)
+ .build()));
}
private String errorMessageForUsage(int usage) {
@@ -540,10 +588,17 @@ public class VibrationSettingsTest {
when(mVibrationConfigMock.getDefaultVibrationIntensity(eq(usage))).thenReturn(intensity);
}
+ private void deleteUserSetting(String settingName) {
+ Settings.System.putStringForUser(
+ mContextSpy.getContentResolver(), settingName, null, UserHandle.USER_CURRENT);
+ // FakeSettingsProvider doesn't support testing triggering ContentObserver yet.
+ mVibrationSettings.updateSettings();
+ }
+
private void setUserSetting(String settingName, int value) {
Settings.System.putIntForUser(
mContextSpy.getContentResolver(), settingName, value, UserHandle.USER_CURRENT);
- // FakeSettingsProvider don't support testing triggering ContentObserver yet.
+ // FakeSettingsProvider doesn't support testing triggering ContentObserver yet.
mVibrationSettings.updateSettings();
}
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibratorControllerTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibratorControllerTest.java
index cb4982be40c3..f2c1874de392 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibratorControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibratorControllerTest.java
@@ -310,13 +310,13 @@ public class VibratorControllerTest {
}
private void mockVibratorCapabilities(int capabilities) {
- VibratorInfo.FrequencyMapping frequencyMapping = new VibratorInfo.FrequencyMapping(
+ VibratorInfo.FrequencyProfile frequencyProfile = new VibratorInfo.FrequencyProfile(
Float.NaN, Float.NaN, Float.NaN, null);
when(mNativeWrapperMock.getInfo(any(VibratorInfo.Builder.class)))
.then(invocation -> {
((VibratorInfo.Builder) invocation.getArgument(0))
.setCapabilities(capabilities)
- .setFrequencyMapping(frequencyMapping);
+ .setFrequencyProfile(frequencyProfile);
return true;
});
}
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
index b0bdaf084b1a..52975ef8bfe1 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
@@ -316,7 +316,7 @@ public class VibratorManagerServiceTest {
assertNotNull(info);
assertEquals(1, info.getId());
- assertEquals(123.f, info.getResonantFrequency(), 0.01 /*tolerance*/);
+ assertEquals(123.f, info.getResonantFrequencyHz(), 0.01 /*tolerance*/);
}
@Test
@@ -341,7 +341,7 @@ public class VibratorManagerServiceTest {
info.isEffectSupported(VibrationEffect.EFFECT_TICK));
assertTrue(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_CLICK));
assertFalse(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_TICK));
- assertEquals(123.f, info.getResonantFrequency(), 0.01 /*tolerance*/);
+ assertEquals(123.f, info.getResonantFrequencyHz(), 0.01 /*tolerance*/);
assertTrue(Float.isNaN(info.getQFactor()));
}
@@ -360,7 +360,7 @@ public class VibratorManagerServiceTest {
VibratorInfo info = createService().getVibratorInfo(1);
assertNotNull(info);
assertEquals(1, info.getId());
- assertEquals(123.f, info.getResonantFrequency(), 0.01 /*tolerance*/);
+ assertEquals(123.f, info.getResonantFrequencyHz(), 0.01 /*tolerance*/);
}
@Test
@@ -1207,6 +1207,33 @@ public class VibratorManagerServiceTest {
assertNotEquals(IExternalVibratorService.SCALE_MUTE, scale);
}
+ @Test
+ public void onExternalVibration_withBypassMuteAudioFlag_ignoresUserSettings() {
+ mockVibrators(1);
+ mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL);
+ setUserSetting(Settings.System.ALARM_VIBRATION_INTENSITY,
+ Vibrator.VIBRATION_INTENSITY_OFF);
+ AudioAttributes audioAttrs = new AudioAttributes.Builder()
+ .setUsage(AudioAttributes.USAGE_ALARM)
+ .build();
+ AudioAttributes flaggedAudioAttrs = new AudioAttributes.Builder()
+ .setUsage(AudioAttributes.USAGE_ALARM)
+ .setFlags(AudioAttributes.FLAG_BYPASS_MUTE)
+ .build();
+ createSystemReadyService();
+
+ int scale = mExternalVibratorService.onExternalVibrationStart(
+ new ExternalVibration(UID, PACKAGE_NAME, audioAttrs,
+ mock(IExternalVibrationController.class)));
+ assertEquals(IExternalVibratorService.SCALE_MUTE, scale);
+
+ createSystemReadyService();
+ scale = mExternalVibratorService.onExternalVibrationStart(
+ new ExternalVibration(UID, PACKAGE_NAME, flaggedAudioAttrs,
+ mock(IExternalVibrationController.class)));
+ assertNotEquals(IExternalVibratorService.SCALE_MUTE, scale);
+ }
+
private VibrationEffectSegment expectedPrebaked(int effectId) {
return new PrebakedSegment(effectId, false, VibrationEffect.EFFECT_STRENGTH_MEDIUM);
}
diff --git a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
index f21991defbec..a12bc3b4c59f 100644
--- a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
@@ -16,14 +16,20 @@
package com.android.server;
+import static android.Manifest.permission.MODIFY_DAY_NIGHT_MODE;
import static android.app.UiModeManager.MODE_NIGHT_AUTO;
import static android.app.UiModeManager.MODE_NIGHT_CUSTOM;
+import static android.app.UiModeManager.MODE_NIGHT_CUSTOM_TYPE_BEDTIME;
+import static android.app.UiModeManager.MODE_NIGHT_CUSTOM_TYPE_SCHEDULE;
+import static android.app.UiModeManager.MODE_NIGHT_CUSTOM_TYPE_UNKNOWN;
import static android.app.UiModeManager.MODE_NIGHT_NO;
import static android.app.UiModeManager.MODE_NIGHT_YES;
import static android.app.UiModeManager.PROJECTION_TYPE_ALL;
import static android.app.UiModeManager.PROJECTION_TYPE_AUTOMOTIVE;
import static android.app.UiModeManager.PROJECTION_TYPE_NONE;
+import static com.google.common.truth.Truth.assertThat;
+
import static junit.framework.TestCase.assertFalse;
import static junit.framework.TestCase.assertTrue;
@@ -194,7 +200,7 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
@Ignore // b/152719290 - Fails on stage-aosp-master
@Test
- public void setNightMoveActivated_overridesFunctionCorrectly() throws RemoteException {
+ public void setNightModeActivated_overridesFunctionCorrectly() throws RemoteException {
// set up
when(mPowerManager.isInteractive()).thenReturn(false);
mService.setNightMode(MODE_NIGHT_NO);
@@ -225,6 +231,29 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
}
@Test
+ public void setNightModeActivated_true_withCustomModeBedtime_shouldOverrideNightModeCorrectly()
+ throws RemoteException {
+ mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
+ assertFalse(mUiManagerService.getConfiguration().isNightModeActive());
+
+ mService.setNightModeActivated(true);
+
+ assertThat(mUiManagerService.getConfiguration().isNightModeActive()).isTrue();
+ }
+
+ @Test
+ public void setNightModeActivated_false_withCustomModeBedtime_shouldOverrideNightModeCorrectly()
+ throws RemoteException {
+ mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
+ assertFalse(mUiManagerService.getConfiguration().isNightModeActive());
+
+ mService.setNightModeActivated(true);
+ mService.setNightModeActivated(false);
+
+ assertThat(mUiManagerService.getConfiguration().isNightModeActive()).isFalse();
+ }
+
+ @Test
public void setAutoMode_screenOffRegistered() throws RemoteException {
try {
mService.setNightMode(MODE_NIGHT_NO);
@@ -247,7 +276,44 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
}
@Test
- public void setNightModeActivated_fromNoToYesAndBAck() throws RemoteException {
+ public void setNightModeCustomType_bedtime_shouldNotActivateNightMode() throws RemoteException {
+ try {
+ mService.setNightMode(MODE_NIGHT_NO);
+ } catch (SecurityException e) { /* we should ignore this update config exception*/ }
+ mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
+
+ assertThat(isNightModeActivated()).isFalse();
+ }
+
+ @Test
+ public void setNightModeCustomType_noPermission_shouldThrow() throws RemoteException {
+ when(mContext.checkCallingOrSelfPermission(eq(MODIFY_DAY_NIGHT_MODE)))
+ .thenReturn(PackageManager.PERMISSION_DENIED);
+
+ assertThrows(SecurityException.class,
+ () -> mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME));
+ }
+
+ @Test
+ public void setNightModeCustomType_bedtime_shouldHaveNoScreenOffRegistered()
+ throws RemoteException {
+ try {
+ mService.setNightMode(MODE_NIGHT_NO);
+ } catch (SecurityException e) { /* we should ignore this update config exception*/ }
+ mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
+ ArgumentCaptor<IntentFilter> intentFiltersCaptor = ArgumentCaptor.forClass(
+ IntentFilter.class);
+ verify(mContext, atLeastOnce()).registerReceiver(any(BroadcastReceiver.class),
+ intentFiltersCaptor.capture());
+
+ List<IntentFilter> intentFilters = intentFiltersCaptor.getAllValues();
+ for (IntentFilter intentFilter : intentFilters) {
+ assertThat(intentFilter.hasAction(Intent.ACTION_SCREEN_OFF)).isFalse();
+ }
+ }
+
+ @Test
+ public void setNightModeActivated_fromNoToYesAndBack() throws RemoteException {
mService.setNightMode(MODE_NIGHT_NO);
mService.setNightModeActivated(true);
assertTrue(isNightModeActivated());
@@ -256,7 +322,7 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
}
@Test
- public void setNightModeActivated_permissiontoChangeOtherUsers() throws RemoteException {
+ public void setNightModeActivated_permissionToChangeOtherUsers() throws RemoteException {
SystemService.TargetUser user = mock(SystemService.TargetUser.class);
doReturn(9).when(user).getUserIdentifier();
mUiManagerService.onUserSwitching(user, user);
@@ -267,6 +333,89 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
}
@Test
+ public void setNightModeActivatedForCustomMode_customTypeBedtime_withParamOnAndBedtime_shouldActivate()
+ throws RemoteException {
+ mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
+ mService.setNightModeActivatedForCustomMode(
+ MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */);
+
+ assertThat(isNightModeActivated()).isTrue();
+ }
+
+ @Test
+ public void setNightModeActivatedForCustomMode_customTypeBedtime_withParamOffAndBedtime_shouldDeactivate()
+ throws RemoteException {
+ mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
+ mService.setNightModeActivatedForCustomMode(
+ MODE_NIGHT_CUSTOM_TYPE_BEDTIME, false /* active */);
+
+ assertThat(isNightModeActivated()).isFalse();
+ }
+
+ @Test
+ public void setNightModeActivatedForCustomMode_customTypeBedtime_withParamOnAndSchedule_shouldNotActivate()
+ throws RemoteException {
+ mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
+ mService.setNightModeActivatedForCustomMode(
+ MODE_NIGHT_CUSTOM_TYPE_SCHEDULE, true /* active */);
+
+ assertThat(isNightModeActivated()).isFalse();
+ }
+
+ @Test
+ public void setNightModeActivatedForCustomMode_customTypeSchedule_withParamOnAndBedtime_shouldNotActivate()
+ throws RemoteException {
+ mService.setNightMode(MODE_NIGHT_CUSTOM);
+ mService.setNightModeActivatedForCustomMode(
+ MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */);
+
+ assertThat(isNightModeActivated()).isFalse();
+ }
+
+ @Test
+ public void setNightModeActivatedForCustomMode_customTypeSchedule_withParamOnAndBedtime_thenCustomTypeBedtime_shouldActivate()
+ throws RemoteException {
+ mService.setNightMode(MODE_NIGHT_CUSTOM);
+ mService.setNightModeActivatedForCustomMode(
+ MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */);
+
+ mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
+
+ assertThat(isNightModeActivated()).isTrue();
+ }
+
+ @Test
+ public void setNightModeActivatedForCustomMode_customTypeBedtime_withParamOnAndBedtime_thenCustomTypeSchedule_shouldKeepNightModeActivate()
+ throws RemoteException {
+ mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
+ mService.setNightModeActivatedForCustomMode(
+ MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */);
+
+ mService.setNightMode(MODE_NIGHT_CUSTOM);
+ LocalTime now = LocalTime.now();
+ mService.setCustomNightModeStart(now.plusHours(1L).toNanoOfDay() / 1000);
+ mService.setCustomNightModeEnd(now.plusHours(2L).toNanoOfDay() / 1000);
+
+ assertThat(isNightModeActivated()).isTrue();
+ }
+
+ @Test
+ public void setNightModeActivatedForCustomMode_customTypeBedtime_withParamOnAndBedtime_thenCustomTypeScheduleAndScreenOff_shouldDeactivateNightMode()
+ throws RemoteException {
+ mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
+ mService.setNightModeActivatedForCustomMode(
+ MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */);
+
+ mService.setNightMode(MODE_NIGHT_CUSTOM);
+ LocalTime now = LocalTime.now();
+ mService.setCustomNightModeStart(now.plusHours(1L).toNanoOfDay() / 1000);
+ mService.setCustomNightModeEnd(now.plusHours(2L).toNanoOfDay() / 1000);
+ mScreenOffCallback.onReceive(mContext, new Intent(Intent.ACTION_SCREEN_OFF));
+
+ assertThat(isNightModeActivated()).isFalse();
+ }
+
+ @Test
public void autoNightModeSwitch_batterySaverOn() throws RemoteException {
mService.setNightMode(MODE_NIGHT_NO);
when(mTwilightState.isNight()).thenReturn(false);
@@ -283,6 +432,191 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
}
@Test
+ public void nightModeCustomBedtime_batterySaverOn_notInBedtime_shouldActivateNightMode()
+ throws RemoteException {
+ mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
+
+ mPowerSaveConsumer.accept(
+ new PowerSaveState.Builder().setBatterySaverEnabled(true).build());
+
+ assertThat(isNightModeActivated()).isTrue();
+ }
+
+ @Test
+ public void nightModeCustomBedtime_batterySaverOn_afterBedtime_shouldKeepNightModeActivated()
+ throws RemoteException {
+ mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
+ mPowerSaveConsumer.accept(
+ new PowerSaveState.Builder().setBatterySaverEnabled(true).build());
+
+ mService.setNightModeActivatedForCustomMode(
+ MODE_NIGHT_CUSTOM_TYPE_BEDTIME, false /* active */);
+
+ assertThat(isNightModeActivated()).isTrue();
+ }
+
+ @Test
+ public void nightModeBedtime_duringBedtime_batterySaverOnThenOff_shouldKeepNightModeActivated()
+ throws RemoteException {
+ mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
+ mService.setNightModeActivatedForCustomMode(
+ MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */);
+
+ mPowerSaveConsumer.accept(
+ new PowerSaveState.Builder().setBatterySaverEnabled(true).build());
+ mPowerSaveConsumer.accept(
+ new PowerSaveState.Builder().setBatterySaverEnabled(false).build());
+
+ assertThat(isNightModeActivated()).isTrue();
+ }
+
+ @Test
+ public void nightModeCustomBedtime_duringBedtime_batterySaverOnThenOff_finallyAfterBedtime_shouldDeactivateNightMode()
+ throws RemoteException {
+ mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
+ mService.setNightModeActivatedForCustomMode(
+ MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */);
+ mPowerSaveConsumer.accept(
+ new PowerSaveState.Builder().setBatterySaverEnabled(true).build());
+ mPowerSaveConsumer.accept(
+ new PowerSaveState.Builder().setBatterySaverEnabled(false).build());
+
+ mService.setNightModeActivatedForCustomMode(
+ MODE_NIGHT_CUSTOM_TYPE_BEDTIME, false /* active */);
+
+ assertThat(isNightModeActivated()).isFalse();
+ }
+
+ @Test
+ public void nightModeCustomBedtime_duringBedtime_changeModeToNo_shouldDeactivateNightMode()
+ throws RemoteException {
+ mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
+ mService.setNightModeActivatedForCustomMode(
+ MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */);
+
+ mService.setNightMode(MODE_NIGHT_NO);
+
+ assertThat(isNightModeActivated()).isFalse();
+ }
+
+ @Test
+ public void nightModeCustomBedtime_duringBedtime_changeModeToNoAndThenExitBedtime_shouldKeepNightModeDeactivated()
+ throws RemoteException {
+ mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
+ mService.setNightModeActivatedForCustomMode(
+ MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */);
+ mService.setNightMode(MODE_NIGHT_NO);
+
+ mService.setNightModeActivatedForCustomMode(
+ MODE_NIGHT_CUSTOM_TYPE_BEDTIME, false /* active */);
+
+ assertThat(isNightModeActivated()).isFalse();
+ }
+
+ @Test
+ public void nightModeCustomBedtime_duringBedtime_changeModeToYes_shouldKeepNightModeActivated()
+ throws RemoteException {
+ mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
+ mService.setNightModeActivatedForCustomMode(
+ MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */);
+
+ mService.setNightMode(MODE_NIGHT_YES);
+
+ assertThat(isNightModeActivated()).isTrue();
+ }
+
+ @Test
+ public void nightModeCustomBedtime_duringBedtime_changeModeToYesAndThenExitBedtime_shouldKeepNightModeActivated()
+ throws RemoteException {
+ mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
+ mService.setNightModeActivatedForCustomMode(
+ MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */);
+
+ mService.setNightMode(MODE_NIGHT_YES);
+ mService.setNightModeActivatedForCustomMode(
+ MODE_NIGHT_CUSTOM_TYPE_BEDTIME, false /* active */);
+
+ assertThat(isNightModeActivated()).isTrue();
+ }
+
+ @Test
+ public void nightModeNo_duringBedtime_shouldKeepNightModeDeactivated()
+ throws RemoteException {
+ mService.setNightMode(MODE_NIGHT_NO);
+
+ mService.setNightModeActivatedForCustomMode(
+ MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */);
+
+ assertThat(isNightModeActivated()).isFalse();
+ }
+
+ @Test
+ public void nightModeNo_thenChangeToCustomTypeBedtimeAndActivate_shouldActivateNightMode()
+ throws RemoteException {
+ mService.setNightMode(MODE_NIGHT_NO);
+
+ mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
+ mService.setNightModeActivatedForCustomMode(
+ MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */);
+
+ assertThat(isNightModeActivated()).isTrue();
+ }
+
+ @Test
+ public void nightModeYes_thenChangeToCustomTypeBedtime_shouldDeactivateNightMode()
+ throws RemoteException {
+ mService.setNightMode(MODE_NIGHT_YES);
+
+ mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
+
+ assertThat(isNightModeActivated()).isFalse();
+ }
+
+ @Test
+ public void nightModeYes_thenChangeToCustomTypeBedtimeAndActivate_shouldActivateNightMode()
+ throws RemoteException {
+ mService.setNightMode(MODE_NIGHT_YES);
+
+ mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
+ mService.setNightModeActivatedForCustomMode(
+ MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */);
+
+ assertThat(isNightModeActivated()).isTrue();
+ }
+
+ @Test
+ public void nightModeAuto_thenChangeToCustomTypeBedtime_notInBedtime_shouldDeactivateNightMode()
+ throws RemoteException {
+ // set mode to auto
+ mService.setNightMode(MODE_NIGHT_AUTO);
+ mService.setNightModeActivated(true);
+ // now it is night time
+ doReturn(true).when(mTwilightState).isNight();
+ mTwilightListener.onTwilightStateChanged(mTwilightState);
+
+ mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
+
+ assertThat(isNightModeActivated()).isFalse();
+ }
+
+ @Test
+ public void nightModeAuto_thenChangeToCustomTypeBedtime_duringBedtime_shouldActivateNightMode()
+ throws RemoteException {
+ // set mode to auto
+ mService.setNightMode(MODE_NIGHT_AUTO);
+ mService.setNightModeActivated(true);
+ // now it is night time
+ doReturn(true).when(mTwilightState).isNight();
+ mTwilightListener.onTwilightStateChanged(mTwilightState);
+
+ mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
+ mService.setNightModeActivatedForCustomMode(
+ MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */);
+
+ assertThat(isNightModeActivated()).isTrue();
+ }
+
+ @Test
public void setAutoMode_clearCache() throws RemoteException {
try {
mService.setNightMode(MODE_NIGHT_AUTO);
@@ -327,6 +661,62 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
}
@Test
+ public void getNightModeCustomType_nightModeNo_shouldReturnUnknown() throws RemoteException {
+ try {
+ mService.setNightMode(MODE_NIGHT_NO);
+ } catch (SecurityException e) { /* we should ignore this update config exception*/ }
+
+ assertThat(mService.getNightModeCustomType()).isEqualTo(MODE_NIGHT_CUSTOM_TYPE_UNKNOWN);
+ }
+
+ @Test
+ public void getNightModeCustomType_nightModeYes_shouldReturnUnknown() throws RemoteException {
+ try {
+ mService.setNightMode(MODE_NIGHT_YES);
+ } catch (SecurityException e) { /* we should ignore this update config exception*/ }
+
+ assertThat(mService.getNightModeCustomType()).isEqualTo(MODE_NIGHT_CUSTOM_TYPE_UNKNOWN);
+ }
+
+ @Test
+ public void getNightModeCustomType_nightModeAuto_shouldReturnUnknown() throws RemoteException {
+ try {
+ mService.setNightMode(MODE_NIGHT_AUTO);
+ } catch (SecurityException e) { /* we should ignore this update config exception*/ }
+
+ assertThat(mService.getNightModeCustomType()).isEqualTo(MODE_NIGHT_CUSTOM_TYPE_UNKNOWN);
+ }
+
+ @Test
+ public void getNightModeCustomType_nightModeCustom_shouldReturnSchedule()
+ throws RemoteException {
+ try {
+ mService.setNightMode(MODE_NIGHT_CUSTOM);
+ } catch (SecurityException e) { /* we should ignore this update config exception*/ }
+
+ assertThat(mService.getNightModeCustomType()).isEqualTo(MODE_NIGHT_CUSTOM_TYPE_SCHEDULE);
+ }
+
+ @Test
+ public void getNightModeCustomType_nightModeCustomBedtime_shouldReturnBedtime()
+ throws RemoteException {
+ try {
+ mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
+ } catch (SecurityException e) { /* we should ignore this update config exception*/ }
+
+ assertThat(mService.getNightModeCustomType()).isEqualTo(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
+ }
+
+ @Test
+ public void getNightModeCustomType_permissionNotGranted_shouldThrow()
+ throws RemoteException {
+ when(mContext.checkCallingOrSelfPermission(eq(MODIFY_DAY_NIGHT_MODE)))
+ .thenReturn(PackageManager.PERMISSION_DENIED);
+
+ assertThrows(SecurityException.class, () -> mService.getNightModeCustomType());
+ }
+
+ @Test
public void isNightModeActive_nightModeYes() throws RemoteException {
try {
mService.setNightMode(MODE_NIGHT_YES);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
index a834e2b6cc5a..12cd834d1d66 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
@@ -394,7 +394,7 @@ public class NotificationListenerServiceTest extends UiServiceTestCase {
"disabledMessage", 0, "disabledMessageResName",
null, null, 0, null, 0, 0,
0, "iconResName", "bitmapPath", null, 0,
- null, null, null);
+ null, null, null, null);
return si;
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index a192bf87cce4..9f92294135c0 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -483,7 +483,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
mAppUsageStats, mock(DevicePolicyManagerInternal.class), mUgm, mUgmInternal,
mAppOpsManager, mAppOpsService, mUm, mHistoryManager, mStatsManager,
mock(TelephonyManager.class),
- mAmi, mToastRateLimiter, mPermissionHelper);
+ mAmi, mToastRateLimiter, mPermissionHelper, mock(UsageStatsManagerInternal.class));
// Return first true for RoleObserver main-thread check
when(mMainLooper.isCurrentThread()).thenReturn(true).thenReturn(false);
mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY, mMainLooper);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java
index f6400b65bc4d..da5496da1bfa 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java
@@ -371,7 +371,8 @@ public class NotificationPermissionMigrationTest extends UiServiceTestCase {
mSnoozeHelper, mUsageStats, mPolicyFile, mActivityManager, mGroupHelper, mAm, mAtm,
mAppUsageStats, mock(DevicePolicyManagerInternal.class), mUgm, mUgmInternal,
mAppOpsManager, mock(IAppOpsService.class), mUm, mHistoryManager, mStatsManager,
- mock(TelephonyManager.class), mAmi, mToastRateLimiter, mPermissionHelper);
+ mock(TelephonyManager.class), mAmi, mToastRateLimiter, mPermissionHelper,
+ mock(UsageStatsManagerInternal.class));
// Return first true for RoleObserver main-thread check
when(mMainLooper.isCurrentThread()).thenReturn(true).thenReturn(false);
mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY, mMainLooper);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index b48c9c3d4bcc..736fbd9d50ef 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -46,6 +46,7 @@ import static com.android.os.AtomsProto.PackageNotificationChannelPreferences.IS
import static com.android.os.AtomsProto.PackageNotificationChannelPreferences.UID_FIELD_NUMBER;
import static com.android.server.notification.PreferencesHelper.DEFAULT_BUBBLE_PREFERENCE;
import static com.android.server.notification.PreferencesHelper.NOTIFICATION_CHANNEL_COUNT_LIMIT;
+import static com.android.server.notification.PreferencesHelper.NOTIFICATION_CHANNEL_GROUP_COUNT_LIMIT;
import static com.android.server.notification.PreferencesHelper.UNKNOWN_UID;
import static com.google.common.truth.Truth.assertThat;
@@ -4250,6 +4251,52 @@ public class PreferencesHelperTest extends UiServiceTestCase {
}
@Test
+ public void testTooManyGroups() {
+ for (int i = 0; i < NOTIFICATION_CHANNEL_GROUP_COUNT_LIMIT; i++) {
+ NotificationChannelGroup group = new NotificationChannelGroup(String.valueOf(i),
+ String.valueOf(i));
+ mHelper.createNotificationChannelGroup(PKG_O, UID_O, group, true);
+ }
+ try {
+ NotificationChannelGroup group = new NotificationChannelGroup(
+ String.valueOf(NOTIFICATION_CHANNEL_GROUP_COUNT_LIMIT),
+ String.valueOf(NOTIFICATION_CHANNEL_GROUP_COUNT_LIMIT));
+ mHelper.createNotificationChannelGroup(PKG_O, UID_O, group, true);
+ fail("Allowed to create too many notification channel groups");
+ } catch (IllegalStateException e) {
+ // great
+ }
+ }
+
+ @Test
+ public void testTooManyGroups_xml() throws Exception {
+ String extraGroup = "EXTRA";
+ String extraGroup1 = "EXTRA1";
+
+ // create first... many... directly so we don't need a big xml blob in this test
+ for (int i = 0; i < NOTIFICATION_CHANNEL_GROUP_COUNT_LIMIT; i++) {
+ NotificationChannelGroup group = new NotificationChannelGroup(String.valueOf(i),
+ String.valueOf(i));
+ mHelper.createNotificationChannelGroup(PKG_O, UID_O, group, true);
+ }
+
+ final String xml = "<ranking version=\"1\">\n"
+ + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n"
+ + "<channelGroup id=\"" + extraGroup + "\" name=\"hi\"/>"
+ + "<channelGroup id=\"" + extraGroup1 + "\" name=\"hi2\"/>"
+ + "</package>"
+ + "</ranking>";
+ TypedXmlPullParser parser = Xml.newFastPullParser();
+ parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())),
+ null);
+ parser.nextTag();
+ mHelper.readXml(parser, false, UserHandle.USER_ALL);
+
+ assertNull(mHelper.getNotificationChannelGroup(extraGroup, PKG_O, UID_O));
+ assertNull(mHelper.getNotificationChannelGroup(extraGroup1, PKG_O, UID_O));
+ }
+
+ @Test
public void testRestoreMultiUser() throws Exception {
String pkg = "restore_pkg";
String channelId = "channelId";
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
index 59d9a35c4471..1d25b54207c4 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
@@ -166,7 +166,8 @@ public class RoleObserverTest extends UiServiceTestCase {
mUm, mock(NotificationHistoryManager.class),
mock(StatsManager.class), mock(TelephonyManager.class),
mock(ActivityManagerInternal.class),
- mock(MultiRateLimiter.class), mock(PermissionHelper.class));
+ mock(MultiRateLimiter.class), mock(PermissionHelper.class),
+ mock(UsageStatsManagerInternal.class));
} catch (SecurityException e) {
if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
throw e;
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 0dcf7992e02b..774e5b9c7fe3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -24,7 +24,6 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT;
import static android.content.pm.ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE;
@@ -571,7 +570,7 @@ public class ActivityRecordTests extends WindowTestsBase {
final ActivityRecord activity = createActivityWith2LevelTask();
final Task task = activity.getTask();
final Task rootTask = activity.getRootTask();
- rootTask.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+ rootTask.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
final Rect stableRect = new Rect();
rootTask.mDisplayContent.getStableRect(stableRect);
@@ -616,7 +615,7 @@ public class ActivityRecordTests extends WindowTestsBase {
spyOn(tda);
doReturn(true).when(tda).supportsNonResizableMultiWindow();
final Task rootTask = mDisplayContent.getDefaultTaskDisplayArea().createRootTask(
- WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, true /* onTop */);
rootTask.setBounds(0, 0, 1000, 500);
final ActivityRecord activity = new ActivityBuilder(mAtm)
.setParentTask(rootTask)
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 a2b04c295944..7c340ecac2c7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -77,6 +77,7 @@ import org.mockito.Mockito;
import org.mockito.MockitoSession;
import java.util.ArrayList;
+import java.util.List;
import java.util.function.Consumer;
/**
@@ -188,6 +189,9 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase {
@Override
public void onFixedRotationFinished(int displayId) {}
+
+ @Override
+ public void onKeepClearAreasChanged(int displayId, List<Rect> keepClearAreas) {}
};
int[] displayIds = mAtm.mWindowManager.registerDisplayWindowListener(listener);
for (int i = 0; i < displayIds.length; i++) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
new file mode 100644
index 000000000000..687779d686d1
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.BackNavigationInfo.typeToString;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.annotation.NonNull;
+import android.hardware.HardwareBuffer;
+import android.platform.test.annotations.Presubmit;
+import android.window.BackNavigationInfo;
+import android.window.TaskSnapshot;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@Presubmit
+@RunWith(WindowTestRunner.class)
+public class BackNavigationControllerTests extends WindowTestsBase {
+
+ private BackNavigationController mBackNavigationController;
+
+ @Before
+ public void setUp() throws Exception {
+ mBackNavigationController = new BackNavigationController();
+ }
+
+ @Test
+ public void backTypeHomeWhenBackToLauncher() {
+ Task task = createTopTaskWithActivity();
+ BackNavigationInfo backNavigationInfo =
+ mBackNavigationController.startBackNavigation(task, new StubTransaction());
+ assertThat(backNavigationInfo).isNotNull();
+ assertThat(typeToString(backNavigationInfo.getType()))
+ .isEqualTo(typeToString(BackNavigationInfo.TYPE_RETURN_TO_HOME));
+ }
+
+ @Test
+ public void backTypeCrossTaskWhenBackToPreviousTask() {
+ Task taskA = createTask(mDefaultDisplay);
+ createActivityRecord(taskA);
+ Task task = createTopTaskWithActivity();
+ BackNavigationInfo backNavigationInfo =
+ mBackNavigationController.startBackNavigation(task, new StubTransaction());
+ assertThat(backNavigationInfo).isNotNull();
+ assertThat(typeToString(backNavigationInfo.getType()))
+ .isEqualTo(typeToString(BackNavigationInfo.TYPE_CROSS_TASK));
+ }
+
+ @Test
+ public void backTypeCrossActivityWhenBackToPreviousActivity() {
+ Task task = createTopTaskWithActivity();
+ mAtm.setFocusedTask(task.mTaskId, createActivityRecord(task));
+ BackNavigationInfo backNavigationInfo =
+ mBackNavigationController.startBackNavigation(task, new StubTransaction());
+ assertThat(backNavigationInfo).isNotNull();
+ assertThat(typeToString(backNavigationInfo.getType()))
+ .isEqualTo(typeToString(BackNavigationInfo.TYPE_CROSS_ACTIVITY));
+ }
+
+ /**
+ * Checks that we are able to fill all the field of the {@link BackNavigationInfo} object.
+ */
+ @Test
+ public void backNavInfoFullyPopulated() {
+ Task task = createTopTaskWithActivity();
+ createActivityRecord(task);
+
+ // We need a mock screenshot so
+ TaskSnapshotController taskSnapshotController = createMockTaskSnapshotController();
+
+ mBackNavigationController.setTaskSnapshotController(taskSnapshotController);
+
+ BackNavigationInfo backNavigationInfo =
+ mBackNavigationController.startBackNavigation(task, new StubTransaction());
+ assertThat(backNavigationInfo).isNotNull();
+ assertThat(backNavigationInfo.getDepartingWindowContainer()).isNotNull();
+ assertThat(backNavigationInfo.getScreenshotSurface()).isNotNull();
+ assertThat(backNavigationInfo.getScreenshotHardwareBuffer()).isNotNull();
+ assertThat(backNavigationInfo.getTaskWindowConfiguration()).isNotNull();
+ }
+
+ @NonNull
+ private TaskSnapshotController createMockTaskSnapshotController() {
+ TaskSnapshotController taskSnapshotController = mock(TaskSnapshotController.class);
+ TaskSnapshot taskSnapshot = mock(TaskSnapshot.class);
+ when(taskSnapshot.getHardwareBuffer()).thenReturn(mock(HardwareBuffer.class));
+ when(taskSnapshotController.getSnapshot(anyInt(), anyInt(), anyBoolean(), anyBoolean()))
+ .thenReturn(taskSnapshot);
+ return taskSnapshotController;
+ }
+
+ @NonNull
+ private Task createTopTaskWithActivity() {
+ Task task = createTask(mDefaultDisplay);
+ ActivityRecord record = createActivityRecord(task);
+ when(record.mSurfaceControl.isValid()).thenReturn(true);
+ mAtm.setFocusedTask(task.mTaskId, record);
+ return task;
+ }
+}
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 2f78b588f305..8d58ec00df96 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -119,6 +119,7 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.os.SystemClock;
import android.platform.test.annotations.Presubmit;
+import android.util.ArraySet;
import android.util.DisplayMetrics;
import android.view.DisplayCutout;
import android.view.DisplayInfo;
@@ -1101,7 +1102,7 @@ public class DisplayContentTests extends WindowTestsBase {
final DisplayContent dc = createNewDisplay();
dc.setImeLayeringTarget(createWindow(null, TYPE_STATUS_BAR, "app"));
dc.getImeTarget(IME_TARGET_LAYERING).getWindow().setWindowingMode(
- WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+ WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW);
assertEquals(dc.getImeContainer().getParentSurfaceControl(), dc.computeImeParent());
}
@@ -1152,7 +1153,7 @@ public class DisplayContentTests extends WindowTestsBase {
final DisplayContent dc = createNewDisplay();
dc.setImeInputTarget(createWindow(null, TYPE_BASE_APPLICATION, "app"));
dc.getImeTarget(IME_TARGET_INPUT).getWindow().setWindowingMode(
- WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+ WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW);
dc.setImeLayeringTarget(dc.getImeTarget(IME_TARGET_INPUT).getWindow());
dc.setRemoteInsetsController(createDisplayWindowInsetsController());
assertNotEquals(dc.getImeTarget(IME_TARGET_INPUT).getWindow(),
@@ -1982,6 +1983,7 @@ public class DisplayContentTests extends WindowTestsBase {
// Test step 1: appWin1 is the current IME target and soft-keyboard is visible.
mDisplayContent.computeImeTarget(true);
assertEquals(appWin1, mDisplayContent.getImeTarget(IME_TARGET_LAYERING));
+ mDisplayContent.setImeInputTarget(appWin1);
spyOn(mDisplayContent.mInputMethodWindow);
doReturn(true).when(mDisplayContent.mInputMethodWindow).isVisible();
mDisplayContent.getInsetsStateController().getImeSourceProvider().setImeShowing(true);
@@ -1998,7 +2000,6 @@ public class DisplayContentTests extends WindowTestsBase {
// be shown at this time.
final Transaction t = mDisplayContent.getPendingTransaction();
spyOn(t);
- mDisplayContent.setImeInputTarget(appWin2);
mDisplayContent.computeImeTarget(true);
assertEquals(appWin2, mDisplayContent.getImeTarget(IME_TARGET_LAYERING));
assertTrue(mDisplayContent.shouldImeAttachedToApp());
@@ -2436,6 +2437,31 @@ public class DisplayContentTests extends WindowTestsBase {
mockSession.finishMocking();
}
+ @Test
+ public void testKeepClearAreasMultipleWindows() {
+ final WindowState w1 = createWindow(null, TYPE_NAVIGATION_BAR, mDisplayContent, "w1");
+ final Rect rect1 = new Rect(0, 0, 10, 10);
+ w1.setKeepClearAreas(Arrays.asList(rect1));
+ final WindowState w2 = createWindow(null, TYPE_NOTIFICATION_SHADE, mDisplayContent, "w2");
+ final Rect rect2 = new Rect(10, 10, 20, 20);
+ w2.setKeepClearAreas(Arrays.asList(rect2));
+
+ // No keep clear areas on display, because the windows are not visible
+ assertEquals(Arrays.asList(), mDisplayContent.getKeepClearAreas());
+
+ makeWindowVisible(w1);
+
+ // The returned keep-clear areas contain the areas just from the visible window
+ assertEquals(new ArraySet(Arrays.asList(rect1)),
+ new ArraySet(mDisplayContent.getKeepClearAreas()));
+
+ makeWindowVisible(w1, w2);
+
+ // The returned keep-clear areas contain the areas from all visible windows
+ assertEquals(new ArraySet(Arrays.asList(rect1, rect2)),
+ new ArraySet(mDisplayContent.getKeepClearAreas()));
+ }
+
private class TestToken extends Binder {
}
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 acf6dc58a597..497ae1defb61 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
@@ -31,10 +31,8 @@ import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-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.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.spy;
@@ -44,7 +42,6 @@ import android.graphics.Insets;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import android.util.Pair;
-import android.view.DisplayCutout;
import android.view.DisplayInfo;
import android.view.InsetsState;
import android.view.PrivacyIndicatorBounds;
@@ -210,24 +207,6 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
expectThrows(IllegalArgumentException.class, () -> addWindow(win2));
}
- @Test
- public void layoutHint_appWindow() {
- mWindow.mAttrs.setFitInsetsTypes(0);
-
- final DisplayCutout.ParcelableWrapper outDisplayCutout =
- new DisplayCutout.ParcelableWrapper();
- final InsetsState outState = new InsetsState();
-
- mDisplayPolicy.getLayoutHint(mWindow.mAttrs, null /* windowToken */, outState,
- true /* localClient */);
-
- assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper()));
- assertThat(outState.getSource(ITYPE_STATUS_BAR).getFrame(),
- is(new Rect(0, 0, DISPLAY_WIDTH, STATUS_BAR_HEIGHT)));
- assertThat(outState.getSource(ITYPE_NAVIGATION_BAR).getFrame(),
- is(new Rect(0, DISPLAY_HEIGHT - NAV_BAR_HEIGHT, DISPLAY_WIDTH, DISPLAY_HEIGHT)));
- }
-
/**
* Verify that {@link DisplayPolicy#simulateLayoutDisplay} outputs the same display frames as
* the real one.
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 407f9cfdbe3e..d64bf121736f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java
@@ -26,11 +26,14 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertNotNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
import android.app.ActivityThread;
import android.content.Context;
@@ -43,6 +46,7 @@ import android.view.Display;
import android.view.IWindowManager;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
+import android.window.WindowTokenClient;
import com.android.server.inputmethod.InputMethodManagerService;
import com.android.server.inputmethod.InputMethodMenuController;
@@ -130,15 +134,31 @@ public class InputMethodMenuControllerTest extends WindowTestsBase {
@Test
public void testGetSettingsContextOnDualDisplayContent() {
final Context context = mController.getSettingsContext(mSecondaryDisplay.getDisplayId());
+ final WindowTokenClient tokenClient = (WindowTokenClient) context.getWindowContextToken();
+ assertNotNull(tokenClient);
+ spyOn(tokenClient);
final DisplayArea.Tokens imeContainer = mSecondaryDisplay.getImeContainer();
+ spyOn(imeContainer);
assertThat(imeContainer.getRootDisplayArea()).isEqualTo(mSecondaryDisplay);
mSecondaryDisplay.mFirstRoot.placeImeContainer(imeContainer);
+
+ verify(imeContainer, atLeastOnce()).onConfigurationChanged(
+ eq(mSecondaryDisplay.mFirstRoot.getConfiguration()));
+ verify(tokenClient, atLeastOnce()).onConfigurationChanged(
+ eq(mSecondaryDisplay.mFirstRoot.getConfiguration()),
+ eq(mSecondaryDisplay.mDisplayId));
assertThat(imeContainer.getRootDisplayArea()).isEqualTo(mSecondaryDisplay.mFirstRoot);
assertImeSwitchContextMetricsValidity(context, mSecondaryDisplay);
mSecondaryDisplay.mSecondRoot.placeImeContainer(imeContainer);
+
+ verify(imeContainer, atLeastOnce()).onConfigurationChanged(
+ eq(mSecondaryDisplay.mSecondRoot.getConfiguration()));
+ verify(tokenClient, atLeastOnce()).onConfigurationChanged(
+ eq(mSecondaryDisplay.mSecondRoot.getConfiguration()),
+ eq(mSecondaryDisplay.mDisplayId));
assertThat(imeContainer.getRootDisplayArea()).isEqualTo(mSecondaryDisplay.mSecondRoot);
assertImeSwitchContextMetricsValidity(context, mSecondaryDisplay);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
index 94bc7f2dc0d1..2eece4c2ca4d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -19,7 +19,6 @@ package com.android.server.wm;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.view.InsetsState.ITYPE_CLIMATE_BAR;
import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_IME;
@@ -246,7 +245,7 @@ public class InsetsStateControllerTest extends WindowTestsBase {
final WindowState child = createWindow(app, TYPE_APPLICATION, "child");
app.mAboveInsetsState.addSource(getController().getRawInsetsState().peekSource(ITYPE_IME));
child.mAttrs.flags |= FLAG_NOT_FOCUSABLE;
- child.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+ child.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
mDisplayContent.computeImeTarget(true);
mDisplayContent.setLayoutNeeded();
diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java
index fc298b0d96a1..0c2de5c6031b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java
@@ -20,7 +20,6 @@ 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_SECONDARY;
import static android.view.Display.INVALID_DISPLAY;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
@@ -334,7 +333,7 @@ public class LaunchParamsControllerTests extends WindowTestsBase {
params.mWindowingMode = windowingMode;
final InstrumentedPositioner positioner = new InstrumentedPositioner(RESULT_DONE, params);
final Task task = new TaskBuilder(mAtm.mTaskSupervisor).setCreateParentTask(true).build();
- task.getRootTask().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+ task.getRootTask().setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
mController.registerModifier(positioner);
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 3cb0bed32493..65b5cf5f13c4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
@@ -88,6 +88,7 @@ import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -529,6 +530,7 @@ public class RootTaskTests extends WindowTestsBase {
// TODO(b/199236198): check this is unnecessary or need to migrate after remove legacy split.
@Test
+ @Ignore
public void testShouldBeVisible_SplitScreen() {
// task not supporting split should be fullscreen for this test.
final Task notSupportingSplitTask = createTaskForShouldBeVisibleTest(
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 4069f0f41d90..f4abf883c219 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -21,8 +21,8 @@ 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.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.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.TYPE_VIRTUAL;
@@ -458,7 +458,7 @@ public class RootWindowContainerTests extends WindowTestsBase {
final TaskDisplayArea defaultTaskDisplayArea = mRootWindowContainer
.getDefaultTaskDisplayArea();
final Task task = defaultTaskDisplayArea.createRootTask(
- WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, true /* onTop */);
final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build();
// Created tasks are focusable by default.
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 c722b0a70c0a..b815c38b7a8a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -77,6 +77,7 @@ import android.content.pm.ActivityInfo;
import android.content.pm.ActivityInfo.ScreenOrientation;
import android.content.res.Configuration;
import android.graphics.Rect;
+import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
import android.provider.DeviceConfig;
import android.provider.DeviceConfig.Properties;
@@ -2182,6 +2183,29 @@ public class SizeCompatTests extends WindowTestsBase {
.computeAspectRatio(sizeCompatAppBounds), delta);
}
+ @Test
+ public void testClearSizeCompat_resetOverrideConfig() {
+ final int origDensity = 480;
+ final int newDensity = 520;
+ final DisplayContent display = new TestDisplayContent.Builder(mAtm, 600, 800)
+ .setDensityDpi(origDensity)
+ .build();
+ setUpApp(display);
+ prepareUnresizable(mActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
+
+ // Activity should enter size compat with old density after display density change.
+ display.setForcedDensity(newDensity, UserHandle.USER_CURRENT);
+
+ assertScaled();
+ assertEquals(origDensity, mActivity.getConfiguration().densityDpi);
+
+ // Activity should exit size compat with new density.
+ mActivity.clearSizeCompatMode();
+
+ assertFitted();
+ assertEquals(newDensity, mActivity.getConfiguration().densityDpi);
+ }
+
private void assertHorizontalPositionForDifferentDisplayConfigsForLandscapeActivity(
float letterboxHorizontalPositionMultiplier) {
// Set up a display in landscape and ignoring orientation request.
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 cdf6b59d4737..80f6bceb884c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
@@ -25,8 +25,6 @@ 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.Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT;
import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
@@ -355,14 +353,10 @@ public class TaskDisplayAreaTests extends WindowTestsBase {
true /* reuseCandidate */);
assertGetOrCreateRootTask(WINDOWING_MODE_UNDEFINED, type, candidateTask,
true /* reuseCandidate */);
- assertGetOrCreateRootTask(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, type, candidateTask,
- true /* reuseCandidate */);
assertGetOrCreateRootTask(WINDOWING_MODE_FREEFORM, type, candidateTask,
true /* reuseCandidate */);
assertGetOrCreateRootTask(WINDOWING_MODE_MULTI_WINDOW, type, candidateTask,
true /* reuseCandidate */);
- assertGetOrCreateRootTask(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, type, candidateTask,
- false /* reuseCandidate */);
assertGetOrCreateRootTask(WINDOWING_MODE_PINNED, type, candidateTask,
true /* reuseCandidate */);
@@ -388,7 +382,7 @@ public class TaskDisplayAreaTests extends WindowTestsBase {
final Task primarySplitTask = new TaskBuilder(mSupervisor)
.setTaskDisplayArea(defaultTaskDisplayArea)
- .setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)
+ .setWindowingMode(WINDOWING_MODE_MULTI_WINDOW)
.setActivityType(ACTIVITY_TYPE_STANDARD)
.setOnTop(true)
.setCreateActivity(true)
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java
index f13847559aa0..64959f240887 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java
@@ -17,8 +17,7 @@
package com.android.server.wm;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-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_MULTI_WINDOW;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
@@ -50,11 +49,8 @@ public class WindowContainerTraversalTests extends WindowTestsBase {
@Test
public void testDockedDividerPosition() {
final WindowState splitScreenWindow = createWindow(null,
- WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION,
+ WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION,
mDisplayContent, "splitScreenWindow");
- final WindowState splitScreenSecondaryWindow = createWindow(null,
- WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD,
- TYPE_BASE_APPLICATION, mDisplayContent, "splitScreenSecondaryWindow");
mDisplayContent.setImeLayeringTarget(splitScreenWindow);
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 75a87ba9e04d..4d5fb6dc5813 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -24,8 +24,6 @@ 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.SCREEN_ORIENTATION_LANDSCAPE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
@@ -520,16 +518,16 @@ public class WindowOrganizerTests extends WindowTestsBase {
DisplayContent dc = mWm.mRoot.getDisplayContent(Display.DEFAULT_DISPLAY);
Task task1 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
- dc, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, null);
+ dc, WINDOWING_MODE_FULLSCREEN, null);
RunningTaskInfo info1 = task1.getTaskInfo();
- assertEquals(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY,
+ assertEquals(WINDOWING_MODE_FULLSCREEN,
info1.configuration.windowConfiguration.getWindowingMode());
assertEquals(ACTIVITY_TYPE_UNDEFINED, info1.topActivityType);
Task task2 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
- dc, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, null);
+ dc, WINDOWING_MODE_MULTI_WINDOW, null);
RunningTaskInfo info2 = task2.getTaskInfo();
- assertEquals(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY,
+ assertEquals(WINDOWING_MODE_MULTI_WINDOW,
info2.configuration.windowConfiguration.getWindowingMode());
assertEquals(ACTIVITY_TYPE_UNDEFINED, info2.topActivityType);
@@ -539,7 +537,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
assertTrue(mWm.mAtmService.mTaskOrganizerController.deleteRootTask(info1.token));
infos = getTasksCreatedByOrganizer(dc);
assertEquals(1, infos.size());
- assertEquals(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, infos.get(0).getWindowingMode());
+ assertEquals(WINDOWING_MODE_MULTI_WINDOW, infos.get(0).getWindowingMode());
}
@Test
@@ -577,7 +575,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
final StubOrganizer listener = new StubOrganizer();
mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(listener);
Task task = mWm.mAtmService.mTaskOrganizerController.createRootTask(
- mDisplayContent, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, null);
+ mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, null);
RunningTaskInfo info1 = task.getTaskInfo();
final Task rootTask = createTask(
@@ -626,7 +624,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
};
mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(listener);
Task task = mWm.mAtmService.mTaskOrganizerController.createRootTask(
- mDisplayContent, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, null);
+ mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, null);
RunningTaskInfo info1 = task.getTaskInfo();
// Ensure events dispatch to organizer.
mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
@@ -684,10 +682,10 @@ public class WindowOrganizerTests extends WindowTestsBase {
mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(listener);
Task task1 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
- mDisplayContent, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, null);
+ mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, null);
RunningTaskInfo info1 = task1.getTaskInfo();
Task task2 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
- mDisplayContent, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, null);
+ mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, null);
RunningTaskInfo info2 = task2.getTaskInfo();
// Ensure events dispatch to organizer.
mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
@@ -1056,7 +1054,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
public void testReparentToOrganizedTask() {
final ITaskOrganizer organizer = registerMockOrganizer();
Task rootTask = mWm.mAtmService.mTaskOrganizerController.createRootTask(
- mDisplayContent, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, null);
+ mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, null);
final Task task1 = createRootTask();
final Task task2 = createTask(rootTask, false /* fakeDraw */);
WindowContainerTransaction wct = new WindowContainerTransaction();
@@ -1223,7 +1221,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
final Task rootTask = activity.getRootTask();
rootTask.setResizeMode(activity.info.resizeMode);
final Task splitPrimaryRootTask = mWm.mAtmService.mTaskOrganizerController.createRootTask(
- mDisplayContent, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, null);
+ mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, null);
final WindowContainerTransaction wct = new WindowContainerTransaction();
wct.reparent(rootTask.mRemoteToken.toWindowContainerToken(),
splitPrimaryRootTask.mRemoteToken.toWindowContainerToken(), true /* onTop */);
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 ec8ec2b31918..80192f7b5d60 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -20,7 +20,6 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
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.view.InsetsState.ITYPE_IME;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
@@ -83,6 +82,7 @@ import android.graphics.Rect;
import android.os.IBinder;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
+import android.util.ArraySet;
import android.view.Gravity;
import android.view.InputWindowHandle;
import android.view.InsetsSource;
@@ -265,22 +265,19 @@ public class WindowStateTests extends WindowTestsBase {
assertFalse(appWindow.canBeImeTarget());
assertFalse(imeWindow.canBeImeTarget());
- // Simulate the window is in split screen primary root task and the current state is
- // minimized and home root task is resizable, so that we should ignore input for the
- // root task.
+ // Simulate the window is in split screen root task.
final DockedTaskDividerController controller =
mDisplayContent.getDockedDividerController();
final Task rootTask = createTask(mDisplayContent,
- WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD);
+ WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
spyOn(appWindow);
spyOn(controller);
spyOn(rootTask);
rootTask.setFocusable(false);
doReturn(rootTask).when(appWindow).getRootTask();
- // Make sure canBeImeTarget is false due to shouldIgnoreInput is true;
+ // Make sure canBeImeTarget is false;
assertFalse(appWindow.canBeImeTarget());
- assertTrue(rootTask.shouldIgnoreInput());
}
@Test
@@ -727,8 +724,9 @@ public class WindowStateTests extends WindowTestsBase {
@Test
public void testCantReceiveTouchWhenNotFocusable() {
final WindowState win0 = createWindow(null, TYPE_APPLICATION, "win0");
- win0.mActivityRecord.getRootTask().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
- win0.mActivityRecord.getRootTask().setFocusable(false);
+ final Task rootTask = win0.mActivityRecord.getRootTask();
+ spyOn(rootTask);
+ when(rootTask.shouldIgnoreInput()).thenReturn(true);
assertFalse(win0.canReceiveTouchInput());
}
@@ -928,8 +926,8 @@ public class WindowStateTests extends WindowTestsBase {
mDisplayContent.setImeLayeringTarget(mAppWindow);
// Simulate entering multi-window mode and verify if the IME control target is remote.
- app.mActivityRecord.getRootTask().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
- assertEquals(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, app.getWindowingMode());
+ app.mActivityRecord.getRootTask().setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+ assertEquals(WINDOWING_MODE_MULTI_WINDOW, app.getWindowingMode());
assertEquals(mDisplayContent.mRemoteInsetsControlTarget,
mDisplayContent.computeImeControlTarget());
@@ -944,14 +942,13 @@ public class WindowStateTests extends WindowTestsBase {
@UseTestDisplay(addWindows = { W_ACTIVITY, W_INPUT_METHOD, W_NOTIFICATION_SHADE })
@Test
- public void testNotificationShadeHasImeInsetsWhenSplitscreenActivated() {
+ public void testNotificationShadeHasImeInsetsWhenMultiWindow() {
WindowState app = createWindow(null, TYPE_BASE_APPLICATION,
mAppWindow.mToken, "app");
- // Simulate entering multi-window mode and verify if the split-screen is activated.
- app.mActivityRecord.getRootTask().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
- assertEquals(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, app.getWindowingMode());
- assertTrue(mDisplayContent.getDefaultTaskDisplayArea().isSplitScreenModeActivated());
+ // Simulate entering multi-window mode and windowing mode is multi-window.
+ app.mActivityRecord.getRootTask().setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+ assertEquals(WINDOWING_MODE_MULTI_WINDOW, app.getWindowingMode());
// Simulate notificationShade is shown and being IME layering target.
mNotificationShadeWindow.setHasSurface(true);
@@ -965,7 +962,7 @@ public class WindowStateTests extends WindowTestsBase {
mDisplayContent.getInsetsStateController().getRawInsetsState()
.setSourceVisible(ITYPE_IME, true);
- // Verify notificationShade can still get IME insets even the split-screen is activated.
+ // Verify notificationShade can still get IME insets even windowing mode is multi-window.
InsetsState state = mDisplayContent.getInsetsStateController().getInsetsForWindow(
mNotificationShadeWindow);
assertNotNull(state.peekSource(ITYPE_IME));
@@ -986,4 +983,40 @@ public class WindowStateTests extends WindowTestsBase {
assertFalse(app.isVisible());
assertTrue(app.isVisibleRequested());
}
+
+ @Test
+ public void testKeepClearAreas() {
+ final WindowState window = createWindow(null, TYPE_APPLICATION, "window");
+ makeWindowVisible(window);
+
+ final Rect keepClearArea1 = new Rect(0, 0, 10, 10);
+ final Rect keepClearArea2 = new Rect(5, 10, 15, 20);
+ final List<Rect> keepClearAreas = Arrays.asList(keepClearArea1, keepClearArea2);
+ window.setKeepClearAreas(keepClearAreas);
+
+ // Test that the keep-clear rects are stored and returned
+ assertEquals(new ArraySet(keepClearAreas), new ArraySet(window.getKeepClearAreas()));
+
+ // Test that keep-clear rects are overwritten
+ window.setKeepClearAreas(Arrays.asList());
+ assertEquals(0, window.getKeepClearAreas().size());
+
+ // Move the window position
+ final SurfaceControl.Transaction t = spy(StubTransaction.class);
+ window.mSurfaceControl = mock(SurfaceControl.class);
+ final Rect frame = window.getFrame();
+ frame.set(10, 20, 60, 80);
+ window.updateSurfacePosition(t);
+ assertEquals(new Point(frame.left, frame.top), window.mLastSurfacePosition);
+
+ // Test that the returned keep-clear rects are translated to display space
+ window.setKeepClearAreas(keepClearAreas);
+ Rect expectedArea1 = new Rect(keepClearArea1);
+ expectedArea1.offset(frame.left, frame.top);
+ Rect expectedArea2 = new Rect(keepClearArea2);
+ expectedArea2.offset(frame.left, frame.top);
+
+ assertEquals(new ArraySet(Arrays.asList(expectedArea1, expectedArea2)),
+ new ArraySet(window.getKeepClearAreas()));
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
index 4dffe7e8345b..0f223ca037ee 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
@@ -22,7 +22,6 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
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.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
@@ -363,7 +362,7 @@ public class ZOrderingTests extends WindowTestsBase {
ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION, mDisplayContent,
"pinnedStackWindow");
final WindowState dockedStackWindow = createWindow(null,
- WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION,
+ WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION,
mDisplayContent, "dockedStackWindow");
final WindowState assistantStackWindow = createWindow(null,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT, TYPE_BASE_APPLICATION,
diff --git a/services/usage/java/com/android/server/usage/StorageStatsService.java b/services/usage/java/com/android/server/usage/StorageStatsService.java
index 88725a6ea1b9..0d88a0d485ff 100644
--- a/services/usage/java/com/android/server/usage/StorageStatsService.java
+++ b/services/usage/java/com/android/server/usage/StorageStatsService.java
@@ -61,6 +61,7 @@ import android.os.storage.CrateMetadata;
import android.os.storage.StorageEventListener;
import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
+import android.provider.DeviceConfig;
import android.provider.Settings;
import android.text.TextUtils;
import android.text.format.DateUtils;
@@ -70,6 +71,7 @@ import android.util.Pair;
import android.util.Slog;
import android.util.SparseLongArray;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.Preconditions;
@@ -128,6 +130,12 @@ public class StorageStatsService extends IStorageStatsManager.Stub {
private final CopyOnWriteArrayList<Pair<String, StorageStatsAugmenter>>
mStorageStatsAugmenters = new CopyOnWriteArrayList<>();
+ @GuardedBy("mLock")
+ private int
+ mStorageThresholdPercentHigh = StorageManager.DEFAULT_STORAGE_THRESHOLD_PERCENT_HIGH;
+
+ private final Object mLock = new Object();
+
public StorageStatsService(Context context) {
mContext = Preconditions.checkNotNull(context);
mAppOps = Preconditions.checkNotNull(context.getSystemService(AppOpsManager.class));
@@ -173,6 +181,19 @@ public class StorageStatsService extends IStorageStatsManager.Stub {
}
}
}, prFilter);
+
+ updateConfig();
+ DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+ mContext.getMainExecutor(), properties -> updateConfig());
+ }
+
+ private void updateConfig() {
+ synchronized (mLock) {
+ mStorageThresholdPercentHigh = DeviceConfig.getInt(
+ DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+ StorageManager.STORAGE_THRESHOLD_PERCENT_HIGH_KEY,
+ StorageManager.DEFAULT_STORAGE_THRESHOLD_PERCENT_HIGH);
+ }
}
private void invalidateMounts() {
@@ -554,7 +575,7 @@ public class StorageStatsService extends IStorageStatsManager.Stub {
* By only triggering a re-calculation after the storage has changed sizes, we can avoid
* recalculating quotas too often. Minimum change delta high and low define the
* percentage of change we need to see before we recalculate quotas when the device has
- * enough storage space (more than StorageManager.STORAGE_THRESHOLD_PERCENT_HIGH of total
+ * enough storage space (more than mStorageThresholdPercentHigh of total
* free) and in low storage condition respectively.
*/
private static final long MINIMUM_CHANGE_DELTA_PERCENT_HIGH = 5;
@@ -588,11 +609,15 @@ public class StorageStatsService extends IStorageStatsManager.Stub {
mStats.restat(Environment.getDataDirectory().getAbsolutePath());
long bytesDelta = Math.abs(mPreviousBytes - mStats.getAvailableBytes());
long bytesDeltaThreshold;
- if (mStats.getAvailableBytes() > mTotalBytes
- * StorageManager.STORAGE_THRESHOLD_PERCENT_HIGH / 100) {
- bytesDeltaThreshold = mTotalBytes * MINIMUM_CHANGE_DELTA_PERCENT_HIGH / 100;
- } else {
- bytesDeltaThreshold = mTotalBytes * MINIMUM_CHANGE_DELTA_PERCENT_LOW / 100;
+ synchronized (mLock) {
+ if (mStats.getAvailableBytes() > mTotalBytes
+ * mStorageThresholdPercentHigh / 100) {
+ bytesDeltaThreshold = mTotalBytes
+ * MINIMUM_CHANGE_DELTA_PERCENT_HIGH / 100;
+ } else {
+ bytesDeltaThreshold = mTotalBytes
+ * MINIMUM_CHANGE_DELTA_PERCENT_LOW / 100;
+ }
}
if (bytesDelta > bytesDeltaThreshold) {
mPreviousBytes = mStats.getAvailableBytes();
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 997c8832f4a9..559eb388fe57 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -33,6 +33,7 @@ import static android.text.format.DateUtils.HOUR_IN_MILLIS;
import android.Manifest;
import android.annotation.CurrentTimeMillisLong;
+import android.annotation.ElapsedRealtimeLong;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -2947,6 +2948,27 @@ public class UsageStatsService extends SystemService implements
@NonNull EstimatedLaunchTimeChangedListener listener) {
UsageStatsService.this.unregisterLaunchTimeChangedListener(listener);
}
+
+ @Override
+ public void reportBroadcastDispatched(int sourceUid, @NonNull String targetPackage,
+ @NonNull UserHandle targetUser, long idForResponseEvent,
+ @ElapsedRealtimeLong long timestampMs) {
+ }
+
+ @Override
+ public void reportNotificationPosted(@NonNull String packageName,
+ @NonNull UserHandle user, @ElapsedRealtimeLong long timestampMs) {
+ }
+
+ @Override
+ public void reportNotificationUpdated(@NonNull String packageName,
+ @NonNull UserHandle user, @ElapsedRealtimeLong long timestampMs) {
+ }
+
+ @Override
+ public void reportNotificationRemoved(@NonNull String packageName,
+ @NonNull UserHandle user, @ElapsedRealtimeLong long timestampMs) {
+ }
}
private class MyPackageMonitor extends PackageMonitor {
diff --git a/services/usb/Android.bp b/services/usb/Android.bp
index 01feacd826c5..3b50fa43536c 100644
--- a/services/usb/Android.bp
+++ b/services/usb/Android.bp
@@ -29,6 +29,7 @@ java_library_static {
"android.hardware.usb-V1.1-java",
"android.hardware.usb-V1.2-java",
"android.hardware.usb-V1.3-java",
+ "android.hardware.usb-V1-java",
"android.hardware.usb.gadget-V1.0-java",
"android.hardware.usb.gadget-V1.1-java",
"android.hardware.usb.gadget-V1.2-java",
diff --git a/services/usb/java/com/android/server/usb/UsbAlsaDevice.java b/services/usb/java/com/android/server/usb/UsbAlsaDevice.java
index 9d4db003a297..85b1de5478e1 100644
--- a/services/usb/java/com/android/server/usb/UsbAlsaDevice.java
+++ b/services/usb/java/com/android/server/usb/UsbAlsaDevice.java
@@ -41,6 +41,7 @@ public final class UsbAlsaDevice {
private final boolean mIsInputHeadset;
private final boolean mIsOutputHeadset;
+ private final boolean mIsDock;
private boolean mSelected = false;
private int mOutputState;
@@ -53,7 +54,7 @@ public final class UsbAlsaDevice {
public UsbAlsaDevice(IAudioService audioService, int card, int device, String deviceAddress,
boolean hasOutput, boolean hasInput,
- boolean isInputHeadset, boolean isOutputHeadset) {
+ boolean isInputHeadset, boolean isOutputHeadset, boolean isDock) {
mAudioService = audioService;
mCardNum = card;
mDeviceNum = device;
@@ -62,31 +63,32 @@ public final class UsbAlsaDevice {
mHasInput = hasInput;
mIsInputHeadset = isInputHeadset;
mIsOutputHeadset = isOutputHeadset;
+ mIsDock = isDock;
}
/**
- * @returns the ALSA card number associated with this peripheral.
+ * @return the ALSA card number associated with this peripheral.
*/
public int getCardNum() {
return mCardNum;
}
/**
- * @returns the ALSA device number associated with this peripheral.
+ * @return the ALSA device number associated with this peripheral.
*/
public int getDeviceNum() {
return mDeviceNum;
}
/**
- * @returns the USB device device address associated with this peripheral.
+ * @return the USB device device address associated with this peripheral.
*/
public String getDeviceAddress() {
return mDeviceAddress;
}
/**
- * @returns the ALSA card/device address string.
+ * @return the ALSA card/device address string.
*/
public String getAlsaCardDeviceString() {
if (mCardNum < 0 || mDeviceNum < 0) {
@@ -98,35 +100,42 @@ public final class UsbAlsaDevice {
}
/**
- * @returns true if the device supports output.
+ * @return true if the device supports output.
*/
public boolean hasOutput() {
return mHasOutput;
}
/**
- * @returns true if the device supports input (recording).
+ * @return true if the device supports input (recording).
*/
public boolean hasInput() {
return mHasInput;
}
/**
- * @returns true if the device is a headset for purposes of input.
+ * @return true if the device is a headset for purposes of input.
*/
public boolean isInputHeadset() {
return mIsInputHeadset;
}
/**
- * @returns true if the device is a headset for purposes of output.
+ * @return true if the device is a headset for purposes of output.
*/
public boolean isOutputHeadset() {
return mIsOutputHeadset;
}
/**
- * @returns true if input jack is detected or jack detection is not supported.
+ * @return true if the device is a USB dock.
+ */
+ public boolean isDock() {
+ return mIsDock;
+ }
+
+ /**
+ * @return true if input jack is detected or jack detection is not supported.
*/
private synchronized boolean isInputJackConnected() {
if (mJackDetector == null) {
@@ -136,7 +145,7 @@ public final class UsbAlsaDevice {
}
/**
- * @returns true if input jack is detected or jack detection is not supported.
+ * @return true if input jack is detected or jack detection is not supported.
*/
private synchronized boolean isOutputJackConnected() {
if (mJackDetector == null) {
@@ -190,9 +199,10 @@ public final class UsbAlsaDevice {
try {
// Output Device
if (mHasOutput) {
- int device = mIsOutputHeadset
- ? AudioSystem.DEVICE_OUT_USB_HEADSET
- : AudioSystem.DEVICE_OUT_USB_DEVICE;
+ int device = mIsDock ? AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET
+ : (mIsOutputHeadset
+ ? AudioSystem.DEVICE_OUT_USB_HEADSET
+ : AudioSystem.DEVICE_OUT_USB_DEVICE);
if (DEBUG) {
Slog.d(TAG, "pre-call device:0x" + Integer.toHexString(device)
+ " addr:" + alsaCardDeviceString
@@ -231,7 +241,7 @@ public final class UsbAlsaDevice {
/**
* @Override
- * @returns a string representation of the object.
+ * @return a string representation of the object.
*/
public synchronized String toString() {
return "UsbAlsaDevice: [card: " + mCardNum
@@ -273,7 +283,7 @@ public final class UsbAlsaDevice {
/**
* @Override
- * @returns true if the objects are equivalent.
+ * @return true if the objects are equivalent.
*/
public boolean equals(Object obj) {
if (!(obj instanceof UsbAlsaDevice)) {
@@ -285,12 +295,13 @@ public final class UsbAlsaDevice {
&& mHasOutput == other.mHasOutput
&& mHasInput == other.mHasInput
&& mIsInputHeadset == other.mIsInputHeadset
- && mIsOutputHeadset == other.mIsOutputHeadset);
+ && mIsOutputHeadset == other.mIsOutputHeadset
+ && mIsDock == other.mIsDock);
}
/**
* @Override
- * @returns a hash code generated from the object contents.
+ * @return a hash code generated from the object contents.
*/
public int hashCode() {
final int prime = 31;
@@ -301,6 +312,7 @@ public final class UsbAlsaDevice {
result = prime * result + (mHasInput ? 0 : 1);
result = prime * result + (mIsInputHeadset ? 0 : 1);
result = prime * result + (mIsOutputHeadset ? 0 : 1);
+ result = prime * result + (mIsDock ? 0 : 1);
return result;
}
diff --git a/services/usb/java/com/android/server/usb/UsbAlsaManager.java b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
index 0aa62c53e269..fd9b9952331a 100644
--- a/services/usb/java/com/android/server/usb/UsbAlsaManager.java
+++ b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
@@ -237,6 +237,7 @@ public final class UsbAlsaManager {
if (hasInput || hasOutput) {
boolean isInputHeadset = parser.isInputHeadset();
boolean isOutputHeadset = parser.isOutputHeadset();
+ boolean isDock = parser.isDock();
if (mAudioService == null) {
Slog.e(TAG, "no AudioService");
@@ -246,7 +247,7 @@ public final class UsbAlsaManager {
UsbAlsaDevice alsaDevice =
new UsbAlsaDevice(mAudioService, cardRec.getCardNum(), 0 /*device*/,
deviceAddress, hasOutput, hasInput,
- isInputHeadset, isOutputHeadset);
+ isInputHeadset, isOutputHeadset, isDock);
if (alsaDevice != null) {
alsaDevice.setDeviceNameAndDescription(
cardRec.getCardName(), cardRec.getCardDescription());
@@ -257,10 +258,11 @@ public final class UsbAlsaManager {
// look for MIDI devices
boolean hasMidi = parser.hasMIDIInterface();
- int midiNumInputs = parser.calculateNumMidiInputs();
- int midiNumOutputs = parser.calculateNumMidiOutputs();
+ int midiNumInputs = parser.calculateNumLegacyMidiInputs();
+ int midiNumOutputs = parser.calculateNumLegacyMidiOutputs();
if (DEBUG) {
Slog.d(TAG, "hasMidi: " + hasMidi + " mHasMidiFeature:" + mHasMidiFeature);
+ Slog.d(TAG, "midiNumInputs: " + midiNumInputs + " midiNumOutputs:" + midiNumOutputs);
}
if (hasMidi && mHasMidiFeature) {
int device = 0;
diff --git a/services/usb/java/com/android/server/usb/UsbHostManager.java b/services/usb/java/com/android/server/usb/UsbHostManager.java
index f33001c9241e..94cc826ffc43 100644
--- a/services/usb/java/com/android/server/usb/UsbHostManager.java
+++ b/services/usb/java/com/android/server/usb/UsbHostManager.java
@@ -23,6 +23,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbDevice;
import android.os.Bundle;
@@ -46,6 +47,8 @@ import com.android.server.usb.descriptors.UsbInterfaceDescriptor;
import com.android.server.usb.descriptors.report.TextReportCanvas;
import com.android.server.usb.descriptors.tree.UsbDescriptorsTree;
+import libcore.io.IoUtils;
+
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
@@ -90,6 +93,13 @@ public class UsbHostManager {
private ConnectionRecord mLastConnect;
private final ArrayMap<String, ConnectionRecord> mConnected = new ArrayMap<>();
+ /**
+ * List of connected MIDI devices
+ */
+ private final HashMap<String, UsbUniversalMidiDevice>
+ mMidiDevices = new HashMap<String, UsbUniversalMidiDevice>();
+ private final boolean mHasMidiFeature;
+
/*
* ConnectionRecord
* Stores connection/disconnection data.
@@ -155,7 +165,7 @@ public class UsbHostManager {
pw.println("manfacturer:0x" + Integer.toHexString(deviceDescriptor.getVendorID())
+ " product:" + Integer.toHexString(deviceDescriptor.getProductID()));
pw.println("isHeadset[in: " + parser.isInputHeadset()
- + " , out: " + parser.isOutputHeadset() + "]");
+ + " , out: " + parser.isOutputHeadset() + "], isDock: " + parser.isDock());
} else {
pw.println(formatTime() + " Disconnect " + mDeviceAddress);
}
@@ -169,9 +179,8 @@ public class UsbHostManager {
UsbDescriptorsTree descriptorTree = new UsbDescriptorsTree();
descriptorTree.parse(parser);
descriptorTree.report(new TextReportCanvas(parser, stringBuilder));
-
stringBuilder.append("isHeadset[in: " + parser.isInputHeadset()
- + " , out: " + parser.isOutputHeadset() + "]");
+ + " , out: " + parser.isOutputHeadset() + "], isDock: " + parser.isDock());
pw.println(stringBuilder.toString());
} else {
pw.println(formatTime() + " Disconnect " + mDeviceAddress);
@@ -188,9 +197,8 @@ public class UsbHostManager {
descriptor.report(canvas);
}
pw.println(stringBuilder.toString());
-
pw.println("isHeadset[in: " + parser.isInputHeadset()
- + " , out: " + parser.isOutputHeadset() + "]");
+ + " , out: " + parser.isOutputHeadset() + "], isDock: " + parser.isDock());
} else {
pw.println(formatTime() + " Disconnect " + mDeviceAddress);
}
@@ -245,6 +253,7 @@ public class UsbHostManager {
setUsbDeviceConnectionHandler(ComponentName.unflattenFromString(
deviceConnectionHandler));
}
+ mHasMidiFeature = context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_MIDI);
}
public void setCurrentUserSettings(UsbProfileGroupSettingsManager settings) {
@@ -413,6 +422,18 @@ public class UsbHostManager {
mUsbAlsaManager.usbDeviceAdded(deviceAddress, newDevice, parser);
+ if (mHasMidiFeature) {
+ if (parser.containsUniversalMidiDeviceEndpoint()) {
+ UsbUniversalMidiDevice midiDevice = UsbUniversalMidiDevice.create(mContext,
+ newDevice, parser);
+ if (midiDevice != null) {
+ mMidiDevices.put(deviceAddress, midiDevice);
+ } else {
+ Slog.e(TAG, "Universal Midi Device is null.");
+ }
+ }
+ }
+
// Tracking
addConnectionRecord(deviceAddress, ConnectionRecord.CONNECT,
parser.getRawDescriptors());
@@ -446,6 +467,14 @@ public class UsbHostManager {
Slog.d(TAG, "Removed device at " + deviceAddress + ": " + device.getProductName());
mUsbAlsaManager.usbDeviceRemoved(deviceAddress);
mPermissionManager.usbDeviceRemoved(device);
+
+ // MIDI
+ UsbUniversalMidiDevice midiDevice = mMidiDevices.remove(deviceAddress);
+ if (midiDevice != null) {
+ Slog.i(TAG, "USB Universal MIDI Device Removed: " + deviceAddress);
+ IoUtils.closeQuietly(midiDevice);
+ }
+
getCurrentUserSettings().usbDeviceRemoved(device);
ConnectionRecord current = mConnected.get(deviceAddress);
// Tracking
diff --git a/services/usb/java/com/android/server/usb/UsbMidiDevice.java b/services/usb/java/com/android/server/usb/UsbMidiDevice.java
index b61e93b36e58..275f21755141 100644
--- a/services/usb/java/com/android/server/usb/UsbMidiDevice.java
+++ b/services/usb/java/com/android/server/usb/UsbMidiDevice.java
@@ -303,7 +303,8 @@ public final class UsbMidiDevice implements Closeable {
}
mServer = midiManager.createDeviceServer(mMidiInputPortReceivers, mNumInputs,
- null, null, properties, MidiDeviceInfo.TYPE_USB, mCallback);
+ null, null, properties, MidiDeviceInfo.TYPE_USB,
+ MidiDeviceInfo.PROTOCOL_UNKNOWN, mCallback);
if (mServer == null) {
return false;
}
diff --git a/services/usb/java/com/android/server/usb/UsbPortManager.java b/services/usb/java/com/android/server/usb/UsbPortManager.java
index ec28040f82d8..65b79bfbb36f 100644
--- a/services/usb/java/com/android/server/usb/UsbPortManager.java
+++ b/services/usb/java/com/android/server/usb/UsbPortManager.java
@@ -16,6 +16,8 @@
package com.android.server.usb;
+import static android.hardware.usb.UsbOperationInternal.USB_OPERATION_ERROR_PORT_MISMATCH;
+import static android.hardware.usb.UsbOperationInternal.USB_OPERATION_ERROR_INTERNAL;
import static android.hardware.usb.UsbPortStatus.CONTAMINANT_DETECTION_NOT_SUPPORTED;
import static android.hardware.usb.UsbPortStatus.CONTAMINANT_PROTECTION_NONE;
import static android.hardware.usb.UsbPortStatus.DATA_ROLE_DEVICE;
@@ -25,6 +27,12 @@ import static android.hardware.usb.UsbPortStatus.MODE_DUAL;
import static android.hardware.usb.UsbPortStatus.MODE_UFP;
import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SINK;
import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SOURCE;
+import static com.android.server.usb.hal.port.UsbPortHal.HAL_POWER_ROLE_SOURCE;
+import static com.android.server.usb.hal.port.UsbPortHal.HAL_POWER_ROLE_SINK;
+import static com.android.server.usb.hal.port.UsbPortHal.HAL_DATA_ROLE_HOST;
+import static com.android.server.usb.hal.port.UsbPortHal.HAL_DATA_ROLE_DEVICE;
+import static com.android.server.usb.hal.port.UsbPortHal.HAL_MODE_DFP;
+import static com.android.server.usb.hal.port.UsbPortHal.HAL_MODE_UFP;
import static com.android.internal.usb.DumpUtils.writePort;
import static com.android.internal.usb.DumpUtils.writePortStatus;
@@ -38,6 +46,7 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
+import android.hardware.usb.IUsbOperationInternal;
import android.hardware.usb.ParcelableUsbPort;
import android.hardware.usb.UsbManager;
import android.hardware.usb.UsbPort;
@@ -74,9 +83,13 @@ import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.dump.DualDumpOutputStream;
import com.android.server.FgThread;
+import com.android.server.usb.hal.port.RawPortInfo;
+import com.android.server.usb.hal.port.UsbPortHal;
+import com.android.server.usb.hal.port.UsbPortHalInstance;
import java.util.ArrayList;
import java.util.NoSuchElementException;
+import java.util.Objects;
/**
* Allows trusted components to control the properties of physical USB ports
@@ -109,16 +122,9 @@ public class UsbPortManager {
// The system context.
private final Context mContext;
- // Proxy object for the usb hal daemon.
- @GuardedBy("mLock")
- private IUsb mProxy = null;
-
// Callback when the UsbPort status is changed by the kernel.
// Mostly due a command sent by the remote Usb device.
- private HALCallback mHALCallback = new HALCallback(null, this);
-
- // Cookie sent for usb hal death notification.
- private static final int USB_HAL_DEATH_COOKIE = 1000;
+ //private HALCallback mHALCallback = new HALCallback(null, this);
// Used as the key while sending the bundle to Main thread.
private static final String PORT_INFO = "port_info";
@@ -156,36 +162,23 @@ public class UsbPortManager {
*/
private int mIsPortContaminatedNotificationId;
- private boolean mEnableUsbDataSignaling;
- protected int mCurrentUsbHalVersion;
+ private UsbPortHal mUsbPortHal;
+
+ private long mTransactionId;
public UsbPortManager(Context context) {
mContext = context;
- try {
- ServiceNotification serviceNotification = new ServiceNotification();
-
- boolean ret = IServiceManager.getService()
- .registerForNotifications("android.hardware.usb@1.0::IUsb",
- "", serviceNotification);
- if (!ret) {
- logAndPrint(Log.ERROR, null,
- "Failed to register service start notification");
- }
- } catch (RemoteException e) {
- logAndPrintException(null,
- "Failed to register service start notification", e);
- return;
- }
- connectToProxy(null);
+ mUsbPortHal = UsbPortHalInstance.getInstance(this, null);
+ logAndPrint(Log.DEBUG, null, "getInstance done");
}
public void systemReady() {
- mSystemReady = true;
- if (mProxy != null) {
+ mSystemReady = true;
+ if (mUsbPortHal != null) {
+ mUsbPortHal.systemReady();
try {
- mProxy.queryPortStatus();
- mEnableUsbDataSignaling = true;
- } catch (RemoteException e) {
+ mUsbPortHal.queryPortStatus(++mTransactionId);
+ } catch (Exception e) {
logAndPrintException(null,
"ServiceStart: Failed to query port status", e);
}
@@ -233,6 +226,7 @@ public class UsbPortManager {
intent.setComponent(ComponentName.unflattenFromString(r.getString(
com.android.internal.R.string.config_usbContaminantActivity)));
intent.putExtra(UsbManager.EXTRA_PORT, ParcelableUsbPort.of(currentPortInfo.mUsbPort));
+ intent.putExtra(UsbManager.EXTRA_PORT_STATUS, currentPortInfo.mUsbPortStatus);
// Simple notification clicks are immutable
PendingIntent pi = PendingIntent.getActivityAsUser(mContext, 0,
@@ -340,61 +334,173 @@ public class UsbPortManager {
}
try {
- // Oneway call into the hal. Use the castFrom method from HIDL.
- android.hardware.usb.V1_2.IUsb proxy = android.hardware.usb.V1_2.IUsb.castFrom(mProxy);
- proxy.enableContaminantPresenceDetection(portId, enable);
- } catch (RemoteException e) {
+ mUsbPortHal.enableContaminantPresenceDetection(portId, enable, ++mTransactionId);
+ } catch (Exception e) {
logAndPrintException(pw, "Failed to set contaminant detection", e);
- } catch (ClassCastException e) {
- logAndPrintException(pw, "Method only applicable to V1.2 or above implementation", e);
}
}
/**
- * Enable/disable the USB data signaling
+ * Limits power transfer in/out of USB-C port.
*
- * @param enable enable or disable USB data signaling
+ * @param portId port identifier.
+ * @param limit limit power transfer when true.
*/
- public boolean enableUsbDataSignal(boolean enable) {
+ public void enableLimitPowerTransfer(@NonNull String portId, boolean limit, long transactionId,
+ IUsbOperationInternal callback, IndentingPrintWriter pw) {
+ Objects.requireNonNull(portId);
+ final PortInfo portInfo = mPorts.get(portId);
+ if (portInfo == null) {
+ logAndPrint(Log.ERROR, pw, "enableLimitPowerTransfer: No such port: " + portId
+ + " opId:" + transactionId);
+ try {
+ if (callback != null) {
+ callback.onOperationComplete(USB_OPERATION_ERROR_PORT_MISMATCH);
+ }
+ } catch (RemoteException e) {
+ logAndPrintException(pw,
+ "enableLimitPowerTransfer: Failed to call OperationComplete. opId:"
+ + transactionId, e);
+ }
+ return;
+ }
+
try {
- mEnableUsbDataSignaling = enable;
- // Call into the hal. Use the castFrom method from HIDL.
- android.hardware.usb.V1_3.IUsb proxy = android.hardware.usb.V1_3.IUsb.castFrom(mProxy);
- return proxy.enableUsbDataSignal(enable);
+ try {
+ mUsbPortHal.enableLimitPowerTransfer(portId, limit, transactionId, callback);
+ } catch (Exception e) {
+ logAndPrintException(pw,
+ "enableLimitPowerTransfer: Failed to limit power transfer. opId:"
+ + transactionId , e);
+ if (callback != null) {
+ callback.onOperationComplete(USB_OPERATION_ERROR_INTERNAL);
+ }
+ }
} catch (RemoteException e) {
- logAndPrintException(null, "Failed to set USB data signaling", e);
- return false;
- } catch (ClassCastException e) {
- logAndPrintException(null, "Method only applicable to V1.3 or above implementation", e);
- return false;
+ logAndPrintException(pw,
+ "enableLimitPowerTransfer:Failed to call onOperationComplete. opId:"
+ + transactionId, e);
}
}
/**
- * Get USB HAL version
+ * Enables USB data when disabled due to {@link UsbPortStatus#USB_DATA_STATUS_DISABLED_DOCK}
+ */
+ public void enableUsbDataWhileDocked(@NonNull String portId, long transactionId,
+ IUsbOperationInternal callback, IndentingPrintWriter pw) {
+ Objects.requireNonNull(portId);
+ final PortInfo portInfo = mPorts.get(portId);
+ if (portInfo == null) {
+ logAndPrint(Log.ERROR, pw, "enableUsbDataWhileDocked: No such port: " + portId
+ + " opId:" + transactionId);
+ try {
+ if (callback != null) {
+ callback.onOperationComplete(USB_OPERATION_ERROR_PORT_MISMATCH);
+ }
+ } catch (RemoteException e) {
+ logAndPrintException(pw,
+ "enableUsbDataWhileDocked: Failed to call OperationComplete. opId:"
+ + transactionId, e);
+ }
+ return;
+ }
+
+ try {
+ try {
+ mUsbPortHal.enableUsbDataWhileDocked(portId, transactionId, callback);
+ } catch (Exception e) {
+ logAndPrintException(pw,
+ "enableUsbDataWhileDocked: Failed to limit power transfer. opId:"
+ + transactionId , e);
+ if (callback != null) {
+ callback.onOperationComplete(USB_OPERATION_ERROR_INTERNAL);
+ }
+ }
+ } catch (RemoteException e) {
+ logAndPrintException(pw,
+ "enableUsbDataWhileDocked:Failed to call onOperationComplete. opId:"
+ + transactionId, e);
+ }
+ }
+
+ /**
+ * Enable/disable the USB data signaling
*
- * @param none
+ * @param enable enable or disable USB data signaling
*/
- public int getUsbHalVersion() {
- return mCurrentUsbHalVersion;
+ public boolean enableUsbData(@NonNull String portId, boolean enable, int transactionId,
+ @NonNull IUsbOperationInternal callback, IndentingPrintWriter pw) {
+ Objects.requireNonNull(callback);
+ Objects.requireNonNull(portId);
+ final PortInfo portInfo = mPorts.get(portId);
+ if (portInfo == null) {
+ logAndPrint(Log.ERROR, pw, "enableUsbData: No such port: " + portId
+ + " opId:" + transactionId);
+ try {
+ callback.onOperationComplete(USB_OPERATION_ERROR_PORT_MISMATCH);
+ } catch (RemoteException e) {
+ logAndPrintException(pw,
+ "enableUsbData: Failed to call OperationComplete. opId:"
+ + transactionId, e);
+ }
+ return false;
+ }
+
+ try {
+ try {
+ return mUsbPortHal.enableUsbData(portId, enable, transactionId, callback);
+ } catch (Exception e) {
+ logAndPrintException(pw,
+ "enableUsbData: Failed to invoke enableUsbData. opId:"
+ + transactionId , e);
+ callback.onOperationComplete(USB_OPERATION_ERROR_INTERNAL);
+ }
+ } catch (RemoteException e) {
+ logAndPrintException(pw,
+ "enableUsbData: Failed to call onOperationComplete. opId:"
+ + transactionId, e);
+ }
+
+ return false;
}
/**
- * update USB HAL version
+ * Get USB HAL version
*
* @param none
+ * @return {@link UsbManager#USB_HAL_RETRY} returned when hal version
+ * is yet to be determined.
*/
- private void updateUsbHalVersion() {
- if (android.hardware.usb.V1_3.IUsb.castFrom(mProxy) != null) {
- mCurrentUsbHalVersion = UsbManager.USB_HAL_V1_3;
- } else if (android.hardware.usb.V1_2.IUsb.castFrom(mProxy) != null) {
- mCurrentUsbHalVersion = UsbManager.USB_HAL_V1_2;
- } else if (android.hardware.usb.V1_1.IUsb.castFrom(mProxy) != null) {
- mCurrentUsbHalVersion = UsbManager.USB_HAL_V1_1;
- } else {
- mCurrentUsbHalVersion = UsbManager.USB_HAL_V1_0;
+ public int getUsbHalVersion() {
+ if (mUsbPortHal != null) {
+ try {
+ return mUsbPortHal.getUsbHalVersion();
+ } catch (RemoteException e) {
+ return UsbManager.USB_HAL_RETRY;
+ }
}
- logAndPrint(Log.INFO, null, "USB HAL version: " + mCurrentUsbHalVersion);
+ return UsbManager.USB_HAL_RETRY;
+ }
+
+ private int toHalUsbDataRole(int usbDataRole) {
+ if (usbDataRole == DATA_ROLE_DEVICE)
+ return HAL_DATA_ROLE_DEVICE;
+ else
+ return HAL_DATA_ROLE_HOST;
+ }
+
+ private int toHalUsbPowerRole(int usbPowerRole) {
+ if (usbPowerRole == POWER_ROLE_SINK)
+ return HAL_POWER_ROLE_SINK;
+ else
+ return HAL_POWER_ROLE_SOURCE;
+ }
+
+ private int toHalUsbMode(int usbMode) {
+ if (usbMode == MODE_UFP)
+ return HAL_MODE_UFP;
+ else
+ return HAL_MODE_DFP;
}
public void setPortRoles(String portId, int newPowerRole, int newDataRole,
@@ -473,7 +579,7 @@ public class UsbPortManager {
sim.currentPowerRole = newPowerRole;
sim.currentDataRole = newDataRole;
updatePortsLocked(pw, null);
- } else if (mProxy != null) {
+ } else if (mUsbPortHal != null) {
if (currentMode != newMode) {
// Changing the mode will have the side-effect of also changing
// the power and data roles but it might take some time to apply
@@ -485,44 +591,37 @@ public class UsbPortManager {
logAndPrint(Log.ERROR, pw, "Trying to set the USB port mode: "
+ "portId=" + portId
+ ", newMode=" + UsbPort.modeToString(newMode));
- PortRole newRole = new PortRole();
- newRole.type = PortRoleType.MODE;
- newRole.role = newMode;
try {
- mProxy.switchRole(portId, newRole);
- } catch (RemoteException e) {
+ mUsbPortHal.switchMode(portId, toHalUsbMode(newMode), ++mTransactionId);
+ } catch (Exception e) {
logAndPrintException(pw, "Failed to set the USB port mode: "
+ "portId=" + portId
- + ", newMode=" + UsbPort.modeToString(newRole.role), e);
+ + ", newMode=" + UsbPort.modeToString(newMode), e);
}
} else {
// Change power and data role independently as needed.
if (currentPowerRole != newPowerRole) {
- PortRole newRole = new PortRole();
- newRole.type = PortRoleType.POWER_ROLE;
- newRole.role = newPowerRole;
try {
- mProxy.switchRole(portId, newRole);
- } catch (RemoteException e) {
+ mUsbPortHal.switchPowerRole(portId, toHalUsbPowerRole(newPowerRole),
+ ++mTransactionId);
+ } catch (Exception e) {
logAndPrintException(pw, "Failed to set the USB port power role: "
+ "portId=" + portId
+ ", newPowerRole=" + UsbPort.powerRoleToString
- (newRole.role),
+ (newPowerRole),
e);
return;
}
}
if (currentDataRole != newDataRole) {
- PortRole newRole = new PortRole();
- newRole.type = PortRoleType.DATA_ROLE;
- newRole.role = newDataRole;
try {
- mProxy.switchRole(portId, newRole);
- } catch (RemoteException e) {
+ mUsbPortHal.switchDataRole(portId, toHalUsbDataRole(newDataRole),
+ ++mTransactionId);
+ } catch (Exception e) {
logAndPrintException(pw, "Failed to set the USB port data role: "
+ "portId=" + portId
- + ", newDataRole=" + UsbPort.dataRoleToString(newRole
- .role),
+ + ", newDataRole=" + UsbPort.dataRoleToString
+ (newDataRole),
e);
}
}
@@ -531,6 +630,15 @@ public class UsbPortManager {
}
}
+ public void updatePorts(ArrayList<RawPortInfo> newPortInfo) {
+ Message message = mHandler.obtainMessage();
+ Bundle bundle = new Bundle();
+ bundle.putParcelableArrayList(PORT_INFO, newPortInfo);
+ message.what = MSG_UPDATE_PORTS;
+ message.setData(bundle);
+ mHandler.sendMessage(message);
+ }
+
public void addSimulatedPort(String portId, int supportedModes, IndentingPrintWriter pw) {
synchronized (mLock) {
if (mSimulatedPorts.containsKey(portId)) {
@@ -662,191 +770,12 @@ public class UsbPortManager {
portInfo.dump(dump, "usb_ports", UsbPortManagerProto.USB_PORTS);
}
- dump.write("enable_usb_data_signaling", UsbPortManagerProto.ENABLE_USB_DATA_SIGNALING,
- mEnableUsbDataSignaling);
+ dump.write("usb_hal_version", UsbPortManagerProto.HAL_VERSION, getUsbHalVersion());
}
dump.end(token);
}
- private static class HALCallback extends IUsbCallback.Stub {
- public IndentingPrintWriter pw;
- public UsbPortManager portManager;
-
- HALCallback(IndentingPrintWriter pw, UsbPortManager portManager) {
- this.pw = pw;
- this.portManager = portManager;
- }
-
- public void notifyPortStatusChange(
- ArrayList<android.hardware.usb.V1_0.PortStatus> currentPortStatus, int retval) {
- if (!portManager.mSystemReady) {
- return;
- }
-
- if (retval != Status.SUCCESS) {
- logAndPrint(Log.ERROR, pw, "port status enquiry failed");
- return;
- }
-
- ArrayList<RawPortInfo> newPortInfo = new ArrayList<>();
-
- for (android.hardware.usb.V1_0.PortStatus current : currentPortStatus) {
- RawPortInfo temp = new RawPortInfo(current.portName,
- current.supportedModes, CONTAMINANT_PROTECTION_NONE,
- current.currentMode,
- current.canChangeMode, current.currentPowerRole,
- current.canChangePowerRole,
- current.currentDataRole, current.canChangeDataRole,
- false, CONTAMINANT_PROTECTION_NONE,
- false, CONTAMINANT_DETECTION_NOT_SUPPORTED);
- newPortInfo.add(temp);
- logAndPrint(Log.INFO, pw, "ClientCallback V1_0: " + current.portName);
- }
-
- Message message = portManager.mHandler.obtainMessage();
- Bundle bundle = new Bundle();
- bundle.putParcelableArrayList(PORT_INFO, newPortInfo);
- message.what = MSG_UPDATE_PORTS;
- message.setData(bundle);
- portManager.mHandler.sendMessage(message);
- }
-
-
- public void notifyPortStatusChange_1_1(ArrayList<PortStatus_1_1> currentPortStatus,
- int retval) {
- if (!portManager.mSystemReady) {
- return;
- }
-
- if (retval != Status.SUCCESS) {
- logAndPrint(Log.ERROR, pw, "port status enquiry failed");
- return;
- }
-
- ArrayList<RawPortInfo> newPortInfo = new ArrayList<>();
-
- int numStatus = currentPortStatus.size();
- for (int i = 0; i < numStatus; i++) {
- PortStatus_1_1 current = currentPortStatus.get(i);
- RawPortInfo temp = new RawPortInfo(current.status.portName,
- current.supportedModes, CONTAMINANT_PROTECTION_NONE,
- current.currentMode,
- current.status.canChangeMode, current.status.currentPowerRole,
- current.status.canChangePowerRole,
- current.status.currentDataRole, current.status.canChangeDataRole,
- false, CONTAMINANT_PROTECTION_NONE,
- false, CONTAMINANT_DETECTION_NOT_SUPPORTED);
- newPortInfo.add(temp);
- logAndPrint(Log.INFO, pw, "ClientCallback V1_1: " + current.status.portName);
- }
-
- Message message = portManager.mHandler.obtainMessage();
- Bundle bundle = new Bundle();
- bundle.putParcelableArrayList(PORT_INFO, newPortInfo);
- message.what = MSG_UPDATE_PORTS;
- message.setData(bundle);
- portManager.mHandler.sendMessage(message);
- }
-
- public void notifyPortStatusChange_1_2(
- ArrayList<PortStatus> currentPortStatus, int retval) {
- if (!portManager.mSystemReady) {
- return;
- }
-
- if (retval != Status.SUCCESS) {
- logAndPrint(Log.ERROR, pw, "port status enquiry failed");
- return;
- }
-
- ArrayList<RawPortInfo> newPortInfo = new ArrayList<>();
-
- int numStatus = currentPortStatus.size();
- for (int i = 0; i < numStatus; i++) {
- PortStatus current = currentPortStatus.get(i);
- RawPortInfo temp = new RawPortInfo(current.status_1_1.status.portName,
- current.status_1_1.supportedModes,
- current.supportedContaminantProtectionModes,
- current.status_1_1.currentMode,
- current.status_1_1.status.canChangeMode,
- current.status_1_1.status.currentPowerRole,
- current.status_1_1.status.canChangePowerRole,
- current.status_1_1.status.currentDataRole,
- current.status_1_1.status.canChangeDataRole,
- current.supportsEnableContaminantPresenceProtection,
- current.contaminantProtectionStatus,
- current.supportsEnableContaminantPresenceDetection,
- current.contaminantDetectionStatus);
- newPortInfo.add(temp);
- logAndPrint(Log.INFO, pw, "ClientCallback V1_2: "
- + current.status_1_1.status.portName);
- }
-
- Message message = portManager.mHandler.obtainMessage();
- Bundle bundle = new Bundle();
- bundle.putParcelableArrayList(PORT_INFO, newPortInfo);
- message.what = MSG_UPDATE_PORTS;
- message.setData(bundle);
- portManager.mHandler.sendMessage(message);
- }
-
- public void notifyRoleSwitchStatus(String portName, PortRole role, int retval) {
- if (retval == Status.SUCCESS) {
- logAndPrint(Log.INFO, pw, portName + " role switch successful");
- } else {
- logAndPrint(Log.ERROR, pw, portName + " role switch failed");
- }
- }
- }
-
- final class DeathRecipient implements HwBinder.DeathRecipient {
- public IndentingPrintWriter pw;
-
- DeathRecipient(IndentingPrintWriter pw) {
- this.pw = pw;
- }
-
- @Override
- public void serviceDied(long cookie) {
- if (cookie == USB_HAL_DEATH_COOKIE) {
- logAndPrint(Log.ERROR, pw, "Usb hal service died cookie: " + cookie);
- synchronized (mLock) {
- mProxy = null;
- }
- }
- }
- }
-
- final class ServiceNotification extends IServiceNotification.Stub {
- @Override
- public void onRegistration(String fqName, String name, boolean preexisting) {
- logAndPrint(Log.INFO, null, "Usb hal service started " + fqName + " " + name);
- connectToProxy(null);
- }
- }
-
- private void connectToProxy(IndentingPrintWriter pw) {
- synchronized (mLock) {
- if (mProxy != null) {
- return;
- }
-
- try {
- mProxy = IUsb.getService();
- mProxy.linkToDeath(new DeathRecipient(pw), USB_HAL_DEATH_COOKIE);
- mProxy.setCallback(mHALCallback);
- mProxy.queryPortStatus();
- updateUsbHalVersion();
- } catch (NoSuchElementException e) {
- logAndPrintException(pw, "connectToProxy: usb hal service not found."
- + " Did the service fail to start?", e);
- } catch (RemoteException e) {
- logAndPrintException(pw, "connectToProxy: usb hal service not responding", e);
- }
- }
- }
-
/**
* Simulated ports directly add the new roles to mSimulatedPorts before calling.
* USB hal callback populates and sends the newPortInfo.
@@ -869,7 +798,10 @@ public class UsbPortManager {
portInfo.supportsEnableContaminantPresenceProtection,
portInfo.contaminantProtectionStatus,
portInfo.supportsEnableContaminantPresenceDetection,
- portInfo.contaminantDetectionStatus, pw);
+ portInfo.contaminantDetectionStatus,
+ portInfo.usbDataStatus,
+ portInfo.powerTransferLimited,
+ portInfo.powerBrickStatus, pw);
}
} else {
for (RawPortInfo currentPortInfo : newPortInfo) {
@@ -881,7 +813,10 @@ public class UsbPortManager {
currentPortInfo.supportsEnableContaminantPresenceProtection,
currentPortInfo.contaminantProtectionStatus,
currentPortInfo.supportsEnableContaminantPresenceDetection,
- currentPortInfo.contaminantDetectionStatus, pw);
+ currentPortInfo.contaminantDetectionStatus,
+ currentPortInfo.usbDataStatus,
+ currentPortInfo.powerTransferLimited,
+ currentPortInfo.powerBrickStatus, pw);
}
}
@@ -917,6 +852,9 @@ public class UsbPortManager {
int contaminantProtectionStatus,
boolean supportsEnableContaminantPresenceDetection,
int contaminantDetectionStatus,
+ int[] usbDataStatus,
+ boolean powerTransferLimited,
+ int powerBrickStatus,
IndentingPrintWriter pw) {
// Only allow mode switch capability for dual role ports.
// Validate that the current mode matches the supported modes we expect.
@@ -975,7 +913,8 @@ public class UsbPortManager {
currentPowerRole, canChangePowerRole,
currentDataRole, canChangeDataRole,
supportedRoleCombinations, contaminantProtectionStatus,
- contaminantDetectionStatus);
+ contaminantDetectionStatus, usbDataStatus,
+ powerTransferLimited, powerBrickStatus);
mPorts.put(portId, portInfo);
} else {
// Validate that ports aren't changing definition out from under us.
@@ -1012,7 +951,8 @@ public class UsbPortManager {
currentPowerRole, canChangePowerRole,
currentDataRole, canChangeDataRole,
supportedRoleCombinations, contaminantProtectionStatus,
- contaminantDetectionStatus)) {
+ contaminantDetectionStatus, usbDataStatus,
+ powerTransferLimited, powerBrickStatus)) {
portInfo.mDisposition = PortInfo.DISPOSITION_CHANGED;
} else {
portInfo.mDisposition = PortInfo.DISPOSITION_READY;
@@ -1034,6 +974,7 @@ public class UsbPortManager {
private void handlePortChangedLocked(PortInfo portInfo, IndentingPrintWriter pw) {
logAndPrint(Log.INFO, pw, "USB port changed: " + portInfo);
enableContaminantDetectionIfNeeded(portInfo, pw);
+ disableLimitPowerTransferIfNeeded(portInfo, pw);
handlePortLocked(portInfo, pw);
}
@@ -1090,6 +1031,19 @@ public class UsbPortManager {
}
}
+ private void disableLimitPowerTransferIfNeeded(PortInfo portInfo, IndentingPrintWriter pw) {
+ if (!mConnected.containsKey(portInfo.mUsbPort.getId())) {
+ return;
+ }
+
+ if (mConnected.get(portInfo.mUsbPort.getId())
+ && !portInfo.mUsbPortStatus.isConnected()
+ && portInfo.mUsbPortStatus.isPowerTransferLimited()) {
+ // Relax enableLimitPowerTransfer upon unplug.
+ enableLimitPowerTransfer(portInfo.mUsbPort.getId(), false, ++mTransactionId, null, pw);
+ }
+ }
+
private void logToStatsd(PortInfo portInfo, IndentingPrintWriter pw) {
// Port is removed
if (portInfo.mUsbPortStatus == null) {
@@ -1141,14 +1095,14 @@ public class UsbPortManager {
}
}
- private static void logAndPrint(int priority, IndentingPrintWriter pw, String msg) {
+ public static void logAndPrint(int priority, IndentingPrintWriter pw, String msg) {
Slog.println(priority, TAG, msg);
if (pw != null) {
pw.println(msg);
}
}
- private static void logAndPrintException(IndentingPrintWriter pw, String msg, Exception e) {
+ public static void logAndPrintException(IndentingPrintWriter pw, String msg, Exception e) {
Slog.e(TAG, msg, e);
if (pw != null) {
pw.println(msg + e);
@@ -1179,7 +1133,7 @@ public class UsbPortManager {
/**
* Describes a USB port.
*/
- private static final class PortInfo {
+ public static final class PortInfo {
public static final int DISPOSITION_ADDED = 0;
public static final int DISPOSITION_CHANGED = 1;
public static final int DISPOSITION_READY = 2;
@@ -1224,7 +1178,9 @@ public class UsbPortManager {
!= supportedRoleCombinations) {
mUsbPortStatus = new UsbPortStatus(currentMode, currentPowerRole, currentDataRole,
supportedRoleCombinations, UsbPortStatus.CONTAMINANT_PROTECTION_NONE,
- UsbPortStatus.CONTAMINANT_DETECTION_NOT_SUPPORTED);
+ UsbPortStatus.CONTAMINANT_DETECTION_NOT_SUPPORTED,
+ new int[]{UsbPortStatus.USB_DATA_STATUS_UNKNOWN}, false,
+ UsbPortStatus.POWER_BRICK_STATUS_UNKNOWN);
dispositionChanged = true;
}
@@ -1239,11 +1195,31 @@ public class UsbPortManager {
return dispositionChanged;
}
+ private boolean dataStatusEquals(int[] dataStatusL, int[] dataStatusR) {
+ if (dataStatusL == null && dataStatusR == null) {
+ return true;
+ }
+ if ((dataStatusL == null && dataStatusR != null)
+ || (dataStatusL != null && dataStatusR == null)) {
+ return false;
+ }
+ if (dataStatusL.length != dataStatusR.length) {
+ return false;
+ }
+ for (int i = 0; i < dataStatusL.length; i++) {
+ if (dataStatusL[i] != dataStatusR[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
public boolean setStatus(int currentMode, boolean canChangeMode,
int currentPowerRole, boolean canChangePowerRole,
int currentDataRole, boolean canChangeDataRole,
int supportedRoleCombinations, int contaminantProtectionStatus,
- int contaminantDetectionStatus) {
+ int contaminantDetectionStatus, int[] usbDataStatus,
+ boolean powerTransferLimited, int powerBrickStatus) {
boolean dispositionChanged = false;
mCanChangeMode = canChangeMode;
@@ -1258,10 +1234,16 @@ public class UsbPortManager {
|| mUsbPortStatus.getContaminantProtectionStatus()
!= contaminantProtectionStatus
|| mUsbPortStatus.getContaminantDetectionStatus()
- != contaminantDetectionStatus) {
+ != contaminantDetectionStatus
+ || !dataStatusEquals(mUsbPortStatus.getUsbDataStatus(), usbDataStatus)
+ || mUsbPortStatus.isPowerTransferLimited()
+ != powerTransferLimited
+ || mUsbPortStatus.getPowerBrickStatus()
+ != powerBrickStatus) {
mUsbPortStatus = new UsbPortStatus(currentMode, currentPowerRole, currentDataRole,
supportedRoleCombinations, contaminantProtectionStatus,
- contaminantDetectionStatus);
+ contaminantDetectionStatus, usbDataStatus,
+ powerTransferLimited, powerBrickStatus);
dispositionChanged = true;
}
@@ -1290,7 +1272,6 @@ public class UsbPortManager {
UsbPortInfoProto.CONNECTED_AT_MILLIS, mConnectedAtMillis);
dump.write("last_connect_duration_millis",
UsbPortInfoProto.LAST_CONNECT_DURATION_MILLIS, mLastConnectDurationMillis);
-
dump.end(token);
}
@@ -1304,115 +1285,4 @@ public class UsbPortManager {
+ ", lastConnectDurationMillis=" + mLastConnectDurationMillis;
}
}
-
- /**
- * Used for storing the raw data from the kernel
- * Values of the member variables mocked directly incase of emulation.
- */
- private static final class RawPortInfo implements Parcelable {
- public final String portId;
- public final int supportedModes;
- public final int supportedContaminantProtectionModes;
- public int currentMode;
- public boolean canChangeMode;
- public int currentPowerRole;
- public boolean canChangePowerRole;
- public int currentDataRole;
- public boolean canChangeDataRole;
- public boolean supportsEnableContaminantPresenceProtection;
- public int contaminantProtectionStatus;
- public boolean supportsEnableContaminantPresenceDetection;
- public int contaminantDetectionStatus;
-
- RawPortInfo(String portId, int supportedModes) {
- this.portId = portId;
- this.supportedModes = supportedModes;
- this.supportedContaminantProtectionModes = UsbPortStatus.CONTAMINANT_PROTECTION_NONE;
- this.supportsEnableContaminantPresenceProtection = false;
- this.contaminantProtectionStatus = UsbPortStatus.CONTAMINANT_PROTECTION_NONE;
- this.supportsEnableContaminantPresenceDetection = false;
- this.contaminantDetectionStatus = UsbPortStatus.CONTAMINANT_DETECTION_NOT_SUPPORTED;
- }
-
- RawPortInfo(String portId, int supportedModes, int supportedContaminantProtectionModes,
- int currentMode, boolean canChangeMode,
- int currentPowerRole, boolean canChangePowerRole,
- int currentDataRole, boolean canChangeDataRole,
- boolean supportsEnableContaminantPresenceProtection,
- int contaminantProtectionStatus,
- boolean supportsEnableContaminantPresenceDetection,
- int contaminantDetectionStatus) {
- this.portId = portId;
- this.supportedModes = supportedModes;
- this.supportedContaminantProtectionModes = supportedContaminantProtectionModes;
- this.currentMode = currentMode;
- this.canChangeMode = canChangeMode;
- this.currentPowerRole = currentPowerRole;
- this.canChangePowerRole = canChangePowerRole;
- this.currentDataRole = currentDataRole;
- this.canChangeDataRole = canChangeDataRole;
- this.supportsEnableContaminantPresenceProtection =
- supportsEnableContaminantPresenceProtection;
- this.contaminantProtectionStatus = contaminantProtectionStatus;
- this.supportsEnableContaminantPresenceDetection =
- supportsEnableContaminantPresenceDetection;
- this.contaminantDetectionStatus = contaminantDetectionStatus;
- }
-
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeString(portId);
- dest.writeInt(supportedModes);
- dest.writeInt(supportedContaminantProtectionModes);
- dest.writeInt(currentMode);
- dest.writeByte((byte) (canChangeMode ? 1 : 0));
- dest.writeInt(currentPowerRole);
- dest.writeByte((byte) (canChangePowerRole ? 1 : 0));
- dest.writeInt(currentDataRole);
- dest.writeByte((byte) (canChangeDataRole ? 1 : 0));
- dest.writeBoolean(supportsEnableContaminantPresenceProtection);
- dest.writeInt(contaminantProtectionStatus);
- dest.writeBoolean(supportsEnableContaminantPresenceDetection);
- dest.writeInt(contaminantDetectionStatus);
- }
-
- public static final Parcelable.Creator<RawPortInfo> CREATOR =
- new Parcelable.Creator<RawPortInfo>() {
- @Override
- public RawPortInfo createFromParcel(Parcel in) {
- String id = in.readString();
- int supportedModes = in.readInt();
- int supportedContaminantProtectionModes = in.readInt();
- int currentMode = in.readInt();
- boolean canChangeMode = in.readByte() != 0;
- int currentPowerRole = in.readInt();
- boolean canChangePowerRole = in.readByte() != 0;
- int currentDataRole = in.readInt();
- boolean canChangeDataRole = in.readByte() != 0;
- boolean supportsEnableContaminantPresenceProtection = in.readBoolean();
- int contaminantProtectionStatus = in.readInt();
- boolean supportsEnableContaminantPresenceDetection = in.readBoolean();
- int contaminantDetectionStatus = in.readInt();
- return new RawPortInfo(id, supportedModes,
- supportedContaminantProtectionModes, currentMode, canChangeMode,
- currentPowerRole, canChangePowerRole,
- currentDataRole, canChangeDataRole,
- supportsEnableContaminantPresenceProtection,
- contaminantProtectionStatus,
- supportsEnableContaminantPresenceDetection,
- contaminantDetectionStatus);
- }
-
- @Override
- public RawPortInfo[] newArray(int size) {
- return new RawPortInfo[size];
- }
- };
- }
}
diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java
index 3d3538d7ae49..88ffc7d613e2 100644
--- a/services/usb/java/com/android/server/usb/UsbService.java
+++ b/services/usb/java/com/android/server/usb/UsbService.java
@@ -16,6 +16,7 @@
package com.android.server.usb;
+import static android.hardware.usb.UsbOperationInternal.USB_OPERATION_ERROR_INTERNAL;
import static android.hardware.usb.UsbPortStatus.DATA_ROLE_DEVICE;
import static android.hardware.usb.UsbPortStatus.DATA_ROLE_HOST;
import static android.hardware.usb.UsbPortStatus.MODE_DFP;
@@ -35,6 +36,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.hardware.usb.IUsbManager;
+import android.hardware.usb.IUsbOperationInternal;
import android.hardware.usb.ParcelableUsbPort;
import android.hardware.usb.UsbAccessory;
import android.hardware.usb.UsbDevice;
@@ -44,6 +46,7 @@ import android.hardware.usb.UsbPortStatus;
import android.os.Binder;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.service.usb.UsbServiceDumpProto;
@@ -731,6 +734,28 @@ public class UsbService extends IUsbManager.Stub {
}
@Override
+ public void enableLimitPowerTransfer(String portId, boolean limit, int operationId,
+ IUsbOperationInternal callback) {
+ Objects.requireNonNull(portId, "portId must not be null. opID:" + operationId);
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ if (mPortManager != null) {
+ mPortManager.enableLimitPowerTransfer(portId, limit, operationId, callback, null);
+ } else {
+ try {
+ callback.onOperationComplete(USB_OPERATION_ERROR_INTERNAL);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "enableLimitPowerTransfer: Failed to call onOperationComplete", e);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override
public void enableContaminantDetection(String portId, boolean enable) {
Objects.requireNonNull(portId, "portId must not be null");
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
@@ -762,15 +787,52 @@ public class UsbService extends IUsbManager.Stub {
}
@Override
- public boolean enableUsbDataSignal(boolean enable) {
+ public boolean enableUsbData(String portId, boolean enable, int operationId,
+ IUsbOperationInternal callback) {
+ Objects.requireNonNull(portId, "enableUsbData: portId must not be null. opId:"
+ + operationId);
+ Objects.requireNonNull(callback, "enableUsbData: callback must not be null. opId:"
+ + operationId);
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
+ final long ident = Binder.clearCallingIdentity();
+ boolean wait;
+ try {
+ if (mPortManager != null) {
+ wait = mPortManager.enableUsbData(portId, enable, operationId, callback, null);
+ } else {
+ wait = false;
+ try {
+ callback.onOperationComplete(USB_OPERATION_ERROR_INTERNAL);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "enableUsbData: Failed to call onOperationComplete", e);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ return wait;
+ }
+ @Override
+ public void enableUsbDataWhileDocked(String portId, int operationId,
+ IUsbOperationInternal callback) {
+ Objects.requireNonNull(portId, "enableUsbDataWhileDocked: portId must not be null. opId:"
+ + operationId);
+ Objects.requireNonNull(callback,
+ "enableUsbDataWhileDocked: callback must not be null. opId:"
+ + operationId);
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
final long ident = Binder.clearCallingIdentity();
+ boolean wait;
try {
if (mPortManager != null) {
- return mPortManager.enableUsbDataSignal(enable);
+ mPortManager.enableUsbDataWhileDocked(portId, operationId, callback, null);
} else {
- return false;
+ try {
+ callback.onOperationComplete(USB_OPERATION_ERROR_INTERNAL);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "enableUsbData: Failed to call onOperationComplete", e);
+ }
}
} finally {
Binder.restoreCallingIdentity(ident);
diff --git a/services/usb/java/com/android/server/usb/UsbUniversalMidiDevice.java b/services/usb/java/com/android/server/usb/UsbUniversalMidiDevice.java
new file mode 100644
index 000000000000..db0c80f189d3
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/UsbUniversalMidiDevice.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.usb;
+
+import android.content.Context;
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbDeviceConnection;
+import android.hardware.usb.UsbEndpoint;
+import android.hardware.usb.UsbManager;
+import android.media.midi.MidiDeviceInfo;
+import android.media.midi.MidiDeviceServer;
+import android.media.midi.MidiDeviceStatus;
+import android.media.midi.MidiManager;
+import android.media.midi.MidiReceiver;
+import android.os.Bundle;
+import android.util.Log;
+
+import com.android.internal.midi.MidiEventScheduler;
+import com.android.internal.midi.MidiEventScheduler.MidiEvent;
+import com.android.server.usb.descriptors.UsbDescriptorParser;
+import com.android.server.usb.descriptors.UsbEndpointDescriptor;
+import com.android.server.usb.descriptors.UsbInterfaceDescriptor;
+import com.android.server.usb.descriptors.UsbMidiBlockParser;
+
+import libcore.io.IoUtils;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.ArrayList;
+
+/**
+ * A MIDI device that opens device connections to MIDI 2.0 endpoints.
+ */
+public final class UsbUniversalMidiDevice implements Closeable {
+ private static final String TAG = "UsbUniversalMidiDevice";
+ private static final boolean DEBUG = false;
+
+ private Context mContext;
+ private UsbDevice mUsbDevice;
+ private UsbDescriptorParser mParser;
+ private ArrayList<UsbInterfaceDescriptor> mUsbInterfaces;
+
+ // USB outputs are MIDI inputs
+ private final InputReceiverProxy[] mMidiInputPortReceivers;
+ private final int mNumInputs;
+ private final int mNumOutputs;
+
+ private MidiDeviceServer mServer;
+
+ // event schedulers for each input port of the physical device
+ private MidiEventScheduler[] mEventSchedulers;
+
+ private ArrayList<UsbDeviceConnection> mUsbDeviceConnections;
+ private ArrayList<ArrayList<UsbEndpoint>> mInputUsbEndpoints;
+ private ArrayList<ArrayList<UsbEndpoint>> mOutputUsbEndpoints;
+
+ private UsbMidiBlockParser mMidiBlockParser = new UsbMidiBlockParser();
+ private int mDefaultMidiProtocol = MidiDeviceInfo.PROTOCOL_UMP_MIDI_1_0_UP_TO_64_BITS;
+
+ private final Object mLock = new Object();
+ private boolean mIsOpen;
+
+ private final MidiDeviceServer.Callback mCallback = new MidiDeviceServer.Callback() {
+
+ @Override
+ public void onDeviceStatusChanged(MidiDeviceServer server, MidiDeviceStatus status) {
+ MidiDeviceInfo deviceInfo = status.getDeviceInfo();
+ int numInputPorts = deviceInfo.getInputPortCount();
+ int numOutputPorts = deviceInfo.getOutputPortCount();
+ boolean hasOpenPorts = false;
+
+ for (int i = 0; i < numInputPorts; i++) {
+ if (status.isInputPortOpen(i)) {
+ hasOpenPorts = true;
+ break;
+ }
+ }
+
+ if (!hasOpenPorts) {
+ for (int i = 0; i < numOutputPorts; i++) {
+ if (status.getOutputPortOpenCount(i) > 0) {
+ hasOpenPorts = true;
+ break;
+ }
+ }
+ }
+
+ synchronized (mLock) {
+ if (hasOpenPorts && !mIsOpen) {
+ openLocked();
+ } else if (!hasOpenPorts && mIsOpen) {
+ closeLocked();
+ }
+ }
+ }
+
+ @Override
+ public void onClose() {
+ }
+ };
+
+ // This class acts as a proxy for our MidiEventScheduler receivers, which do not exist
+ // until the device has active clients
+ private static final class InputReceiverProxy extends MidiReceiver {
+ private MidiReceiver mReceiver;
+
+ @Override
+ public void onSend(byte[] msg, int offset, int count, long timestamp) throws IOException {
+ MidiReceiver receiver = mReceiver;
+ if (receiver != null) {
+ receiver.send(msg, offset, count, timestamp);
+ }
+ }
+
+ public void setReceiver(MidiReceiver receiver) {
+ mReceiver = receiver;
+ }
+
+ @Override
+ public void onFlush() throws IOException {
+ MidiReceiver receiver = mReceiver;
+ if (receiver != null) {
+ receiver.flush();
+ }
+ }
+ }
+
+ /**
+ * Creates an UsbUniversalMidiDevice based on the input parameters. Read/Write streams
+ * will be created individually as some devices don't have the same number of
+ * inputs and outputs.
+ */
+ public static UsbUniversalMidiDevice create(Context context, UsbDevice usbDevice,
+ UsbDescriptorParser parser) {
+ UsbUniversalMidiDevice midiDevice = new UsbUniversalMidiDevice(usbDevice, parser);
+ if (!midiDevice.register(context)) {
+ IoUtils.closeQuietly(midiDevice);
+ Log.e(TAG, "createDeviceServer failed");
+ return null;
+ }
+ return midiDevice;
+ }
+
+ private UsbUniversalMidiDevice(UsbDevice usbDevice, UsbDescriptorParser parser) {
+ mUsbDevice = usbDevice;
+ mParser = parser;
+
+ mUsbInterfaces = parser.findUniversalMidiInterfaceDescriptors();
+
+ int numInputs = 0;
+ int numOutputs = 0;
+
+ for (int interfaceIndex = 0; interfaceIndex < mUsbInterfaces.size(); interfaceIndex++) {
+ UsbInterfaceDescriptor interfaceDescriptor = mUsbInterfaces.get(interfaceIndex);
+ for (int endpointIndex = 0; endpointIndex < interfaceDescriptor.getNumEndpoints();
+ endpointIndex++) {
+ UsbEndpointDescriptor endpoint =
+ interfaceDescriptor.getEndpointDescriptor(endpointIndex);
+ // 0 is output, 1 << 7 is input.
+ if (endpoint.getDirection() == 0) {
+ numOutputs++;
+ } else {
+ numInputs++;
+ }
+ }
+ }
+
+ mNumInputs = numInputs;
+ mNumOutputs = numOutputs;
+
+ Log.d(TAG, "Created UsbUniversalMidiDevice with " + numInputs + " inputs and "
+ + numOutputs + " outputs");
+
+ // Create MIDI port receivers based on the number of output ports. The
+ // output of USB is the input of MIDI.
+ mMidiInputPortReceivers = new InputReceiverProxy[numOutputs];
+ for (int port = 0; port < numOutputs; port++) {
+ mMidiInputPortReceivers[port] = new InputReceiverProxy();
+ }
+ }
+
+ private int calculateDefaultMidiProtocol() {
+ UsbManager manager = mContext.getSystemService(UsbManager.class);
+
+ for (int interfaceIndex = 0; interfaceIndex < mUsbInterfaces.size(); interfaceIndex++) {
+ UsbInterfaceDescriptor interfaceDescriptor = mUsbInterfaces.get(interfaceIndex);
+ boolean doesInterfaceContainInput = false;
+ boolean doesInterfaceContainOutput = false;
+ for (int endpointIndex = 0; (endpointIndex < interfaceDescriptor.getNumEndpoints())
+ && !(doesInterfaceContainInput && doesInterfaceContainOutput);
+ endpointIndex++) {
+ UsbEndpointDescriptor endpoint =
+ interfaceDescriptor.getEndpointDescriptor(endpointIndex);
+ // 0 is output, 1 << 7 is input.
+ if (endpoint.getDirection() == 0) {
+ doesInterfaceContainOutput = true;
+ } else {
+ doesInterfaceContainInput = true;
+ }
+ }
+
+ // Intentionally open the device connection to query the default MIDI type for
+ // a connection with both the input and output set.
+ if (doesInterfaceContainInput
+ && doesInterfaceContainOutput) {
+ UsbDeviceConnection connection = manager.openDevice(mUsbDevice);
+ if (!connection.claimInterface(interfaceDescriptor.toAndroid(mParser), true)) {
+ Log.d(TAG, "Can't claim control interface");
+ continue;
+ }
+ int defaultMidiProtocol = mMidiBlockParser.calculateMidiType(connection,
+ interfaceDescriptor.getInterfaceNumber(),
+ interfaceDescriptor.getAlternateSetting());
+
+ connection.close();
+ return defaultMidiProtocol;
+ }
+ }
+
+ Log.d(TAG, "Cannot find interface with both input and output endpoints");
+ return MidiDeviceInfo.PROTOCOL_UMP_MIDI_1_0_UP_TO_64_BITS;
+ }
+
+ private boolean openLocked() {
+ UsbManager manager = mContext.getSystemService(UsbManager.class);
+
+ mUsbDeviceConnections = new ArrayList<UsbDeviceConnection>(mUsbInterfaces.size());
+ mInputUsbEndpoints = new ArrayList<ArrayList<UsbEndpoint>>(mUsbInterfaces.size());
+ mOutputUsbEndpoints = new ArrayList<ArrayList<UsbEndpoint>>(mUsbInterfaces.size());
+
+ for (int interfaceIndex = 0; interfaceIndex < mUsbInterfaces.size(); interfaceIndex++) {
+ ArrayList<UsbEndpoint> inputEndpoints = new ArrayList<UsbEndpoint>();
+ ArrayList<UsbEndpoint> outputEndpoints = new ArrayList<UsbEndpoint>();
+ UsbInterfaceDescriptor interfaceDescriptor = mUsbInterfaces.get(interfaceIndex);
+ for (int endpointIndex = 0; endpointIndex < interfaceDescriptor.getNumEndpoints();
+ endpointIndex++) {
+ UsbEndpointDescriptor endpoint =
+ interfaceDescriptor.getEndpointDescriptor(endpointIndex);
+ // 0 is output, 1 << 7 is input.
+ if (endpoint.getDirection() == 0) {
+ outputEndpoints.add(endpoint.toAndroid(mParser));
+ } else {
+ inputEndpoints.add(endpoint.toAndroid(mParser));
+ }
+ }
+ if (!outputEndpoints.isEmpty() || !inputEndpoints.isEmpty()) {
+ UsbDeviceConnection connection = manager.openDevice(mUsbDevice);
+ connection.setInterface(interfaceDescriptor.toAndroid(mParser));
+ connection.claimInterface(interfaceDescriptor.toAndroid(mParser), true);
+ mUsbDeviceConnections.add(connection);
+ mInputUsbEndpoints.add(inputEndpoints);
+ mOutputUsbEndpoints.add(outputEndpoints);
+ }
+ }
+
+ mEventSchedulers = new MidiEventScheduler[mNumOutputs];
+
+ for (int i = 0; i < mNumOutputs; i++) {
+ MidiEventScheduler scheduler = new MidiEventScheduler();
+ mEventSchedulers[i] = scheduler;
+ mMidiInputPortReceivers[i].setReceiver(scheduler.getReceiver());
+ }
+
+ final MidiReceiver[] outputReceivers = mServer.getOutputPortReceivers();
+
+ // Create input thread for each input port of the physical device
+ int portNumber = 0;
+ for (int connectionIndex = 0; connectionIndex < mInputUsbEndpoints.size();
+ connectionIndex++) {
+ for (int endpointIndex = 0;
+ endpointIndex < mInputUsbEndpoints.get(connectionIndex).size();
+ endpointIndex++) {
+ final UsbDeviceConnection connectionF = mUsbDeviceConnections.get(connectionIndex);
+ final UsbEndpoint epF = mInputUsbEndpoints.get(connectionIndex).get(endpointIndex);
+ final int portF = portNumber;
+
+ new Thread("UsbUniversalMidiDevice input thread " + portF) {
+ @Override
+ public void run() {
+ byte[] inputBuffer = new byte[epF.getMaxPacketSize()];
+ try {
+ while (true) {
+ // Record time of event immediately after waking.
+ long timestamp = System.nanoTime();
+ synchronized (mLock) {
+ if (!mIsOpen) break;
+
+ int nRead = connectionF.bulkTransfer(epF, inputBuffer,
+ inputBuffer.length, 0);
+
+ // For USB, each 32 bit word of a UMP is
+ // sent with the least significant byte first.
+ swapEndiannessPerWord(inputBuffer, inputBuffer.length);
+
+ if (nRead > 0) {
+ if (DEBUG) {
+ logByteArray("Input ", inputBuffer, 0,
+ nRead);
+ }
+ outputReceivers[portF].send(inputBuffer, 0, nRead,
+ timestamp);
+ }
+ }
+ }
+ } catch (IOException e) {
+ Log.d(TAG, "reader thread exiting");
+ }
+ Log.d(TAG, "input thread exit");
+ }
+ }.start();
+
+ portNumber++;
+ }
+ }
+
+ // Create output thread for each output port of the physical device
+ portNumber = 0;
+ for (int connectionIndex = 0; connectionIndex < mOutputUsbEndpoints.size();
+ connectionIndex++) {
+ for (int endpointIndex = 0;
+ endpointIndex < mOutputUsbEndpoints.get(connectionIndex).size();
+ endpointIndex++) {
+ final UsbDeviceConnection connectionF = mUsbDeviceConnections.get(connectionIndex);
+ final UsbEndpoint epF =
+ mOutputUsbEndpoints.get(connectionIndex).get(endpointIndex);
+ final int portF = portNumber;
+ final MidiEventScheduler eventSchedulerF = mEventSchedulers[portF];
+
+ new Thread("UsbUniversalMidiDevice output thread " + portF) {
+ @Override
+ public void run() {
+ while (true) {
+ MidiEvent event;
+ try {
+ event = (MidiEvent) eventSchedulerF.waitNextEvent();
+ } catch (InterruptedException e) {
+ // try again
+ continue;
+ }
+ if (event == null) {
+ break;
+ }
+
+ // For USB, each 32 bit word of a UMP is
+ // sent with the least significant byte first.
+ swapEndiannessPerWord(event.data, event.count);
+
+ if (DEBUG) {
+ logByteArray("Output ", event.data, 0,
+ event.count);
+ }
+ connectionF.bulkTransfer(epF, event.data, event.count, 0);
+ eventSchedulerF.addEventToPool(event);
+ }
+ Log.d(TAG, "output thread exit");
+ }
+ }.start();
+
+ portNumber++;
+ }
+ }
+
+ mIsOpen = true;
+ return true;
+ }
+
+ private boolean register(Context context) {
+ mContext = context;
+ MidiManager midiManager = context.getSystemService(MidiManager.class);
+ if (midiManager == null) {
+ Log.e(TAG, "No MidiManager in UsbUniversalMidiDevice.create()");
+ return false;
+ }
+
+ mDefaultMidiProtocol = calculateDefaultMidiProtocol();
+
+ Bundle properties = new Bundle();
+ String manufacturer = mUsbDevice.getManufacturerName();
+ String product = mUsbDevice.getProductName();
+ String version = mUsbDevice.getVersion();
+ String name;
+ if (manufacturer == null || manufacturer.isEmpty()) {
+ name = product;
+ } else if (product == null || product.isEmpty()) {
+ name = manufacturer;
+ } else {
+ name = manufacturer + " " + product + " MIDI 2.0";
+ }
+ properties.putString(MidiDeviceInfo.PROPERTY_NAME, name);
+ properties.putString(MidiDeviceInfo.PROPERTY_MANUFACTURER, manufacturer);
+ properties.putString(MidiDeviceInfo.PROPERTY_PRODUCT, product);
+ properties.putString(MidiDeviceInfo.PROPERTY_VERSION, version);
+ properties.putString(MidiDeviceInfo.PROPERTY_SERIAL_NUMBER,
+ mUsbDevice.getSerialNumber());
+ properties.putParcelable(MidiDeviceInfo.PROPERTY_USB_DEVICE, mUsbDevice);
+
+ mServer = midiManager.createDeviceServer(mMidiInputPortReceivers, mNumInputs,
+ null, null, properties, MidiDeviceInfo.TYPE_USB, mDefaultMidiProtocol, mCallback);
+ if (mServer == null) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public void close() throws IOException {
+ synchronized (mLock) {
+ if (mIsOpen) {
+ closeLocked();
+ }
+ }
+
+ if (mServer != null) {
+ IoUtils.closeQuietly(mServer);
+ }
+ }
+
+ private void closeLocked() {
+ for (int i = 0; i < mEventSchedulers.length; i++) {
+ mMidiInputPortReceivers[i].setReceiver(null);
+ mEventSchedulers[i].close();
+ }
+ for (UsbDeviceConnection connection : mUsbDeviceConnections) {
+ connection.close();
+ }
+ mUsbDeviceConnections = null;
+ mInputUsbEndpoints = null;
+ mOutputUsbEndpoints = null;
+
+ mIsOpen = false;
+ }
+
+ private void swapEndiannessPerWord(byte[] array, int size) {
+ for (int i = 0; i + 3 < size; i += 4) {
+ byte tmp = array[i];
+ array[i] = array[i + 3];
+ array[i + 3] = tmp;
+ tmp = array[i + 1];
+ array[i + 1] = array[i + 2];
+ array[i + 2] = tmp;
+ }
+ }
+
+ private static void logByteArray(String prefix, byte[] value, int offset, int count) {
+ StringBuilder builder = new StringBuilder(prefix);
+ for (int i = offset; i < offset + count; i++) {
+ builder.append(String.format("0x%02X", value[i]));
+ if (i != value.length - 1) {
+ builder.append(", ");
+ }
+ }
+ Log.d(TAG, builder.toString());
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbACAudioControlEndpoint.java b/services/usb/java/com/android/server/usb/descriptors/UsbACAudioControlEndpoint.java
index 409e605c3c2f..bfcf62147f75 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbACAudioControlEndpoint.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbACAudioControlEndpoint.java
@@ -38,8 +38,8 @@ public class UsbACAudioControlEndpoint extends UsbACEndpoint {
static final byte ATTRIBSMASK_SYNC = 0x0C;
static final byte ATTRIBMASK_TRANS = 0x03;
- public UsbACAudioControlEndpoint(int length, byte type, int subclass) {
- super(length, type, subclass);
+ public UsbACAudioControlEndpoint(int length, byte type, int subclass, byte subtype) {
+ super(length, type, subclass, subtype);
}
public byte getAddress() {
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbACAudioStreamEndpoint.java b/services/usb/java/com/android/server/usb/descriptors/UsbACAudioStreamEndpoint.java
index e63bb74abdf7..ae9ca0d827f5 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbACAudioStreamEndpoint.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbACAudioStreamEndpoint.java
@@ -24,8 +24,8 @@ public class UsbACAudioStreamEndpoint extends UsbACEndpoint {
private static final String TAG = "UsbACAudioStreamEndpoint";
//TODO data fields...
- public UsbACAudioStreamEndpoint(int length, byte type, int subclass) {
- super(length, type, subclass);
+ public UsbACAudioStreamEndpoint(int length, byte type, int subclass, byte subtype) {
+ super(length, type, subclass, subtype);
}
@Override
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbACEndpoint.java b/services/usb/java/com/android/server/usb/descriptors/UsbACEndpoint.java
index ff7f3934086c..b7f9ac334954 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbACEndpoint.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbACEndpoint.java
@@ -25,13 +25,17 @@ import android.util.Log;
abstract class UsbACEndpoint extends UsbDescriptor {
private static final String TAG = "UsbACEndpoint";
+ public static final byte MS_GENERAL = 1;
+ public static final byte MS_GENERAL_2_0 = 2;
+
protected final int mSubclass; // from the mSubclass member of the "enclosing"
// Interface Descriptor, not the stream.
- protected byte mSubtype; // 2:1 HEADER descriptor subtype
+ protected final byte mSubtype; // 2:1 HEADER descriptor subtype
- UsbACEndpoint(int length, byte type, int subclass) {
+ UsbACEndpoint(int length, byte type, int subclass, byte subtype) {
super(length, type);
mSubclass = subclass;
+ mSubtype = subtype;
}
public int getSubclass() {
@@ -44,33 +48,39 @@ abstract class UsbACEndpoint extends UsbDescriptor {
@Override
public int parseRawDescriptors(ByteStream stream) {
- mSubtype = stream.getByte();
return mLength;
}
public static UsbDescriptor allocDescriptor(UsbDescriptorParser parser,
- int length, byte type) {
+ int length, byte type, byte subType) {
UsbInterfaceDescriptor interfaceDesc = parser.getCurInterface();
int subClass = interfaceDesc.getUsbSubclass();
- // TODO shouldn't this switch on subtype?
switch (subClass) {
case AUDIO_AUDIOCONTROL:
if (UsbDescriptorParser.DEBUG) {
Log.d(TAG, "---> AUDIO_AUDIOCONTROL");
}
- return new UsbACAudioControlEndpoint(length, type, subClass);
+ return new UsbACAudioControlEndpoint(length, type, subClass, subType);
case AUDIO_AUDIOSTREAMING:
if (UsbDescriptorParser.DEBUG) {
Log.d(TAG, "---> AUDIO_AUDIOSTREAMING");
}
- return new UsbACAudioStreamEndpoint(length, type, subClass);
+ return new UsbACAudioStreamEndpoint(length, type, subClass, subType);
case AUDIO_MIDISTREAMING:
if (UsbDescriptorParser.DEBUG) {
Log.d(TAG, "---> AUDIO_MIDISTREAMING");
}
- return new UsbACMidiEndpoint(length, type, subClass);
+ switch (subType) {
+ case MS_GENERAL:
+ return new UsbACMidi10Endpoint(length, type, subClass, subType);
+ case MS_GENERAL_2_0:
+ return new UsbACMidi20Endpoint(length, type, subClass, subType);
+ default:
+ Log.w(TAG, "Unknown Midi Endpoint id:0x" + Integer.toHexString(subType));
+ return null;
+ }
default:
Log.w(TAG, "Unknown Audio Class Endpoint id:0x" + Integer.toHexString(subClass));
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbACMidiEndpoint.java b/services/usb/java/com/android/server/usb/descriptors/UsbACMidi10Endpoint.java
index 42ee88922edd..49b9d7b30d29 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbACMidiEndpoint.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbACMidi10Endpoint.java
@@ -22,14 +22,14 @@ import com.android.server.usb.descriptors.report.ReportCanvas;
* An audio class-specific Midi Endpoint.
* see midi10.pdf section 6.2.2
*/
-public final class UsbACMidiEndpoint extends UsbACEndpoint {
- private static final String TAG = "UsbACMidiEndpoint";
+public final class UsbACMidi10Endpoint extends UsbACEndpoint {
+ private static final String TAG = "UsbACMidi10Endpoint";
private byte mNumJacks;
- private byte[] mJackIds;
+ private byte[] mJackIds = new byte[0];
- public UsbACMidiEndpoint(int length, byte type, int subclass) {
- super(length, type, subclass);
+ public UsbACMidi10Endpoint(int length, byte type, int subclass, byte subtype) {
+ super(length, type, subclass, subtype);
}
public byte getNumJacks() {
@@ -45,9 +45,11 @@ public final class UsbACMidiEndpoint extends UsbACEndpoint {
super.parseRawDescriptors(stream);
mNumJacks = stream.getByte();
- mJackIds = new byte[mNumJacks];
- for (int jack = 0; jack < mNumJacks; jack++) {
- mJackIds[jack] = stream.getByte();
+ if (mNumJacks > 0) {
+ mJackIds = new byte[mNumJacks];
+ for (int jack = 0; jack < mNumJacks; jack++) {
+ mJackIds[jack] = stream.getByte();
+ }
}
return mLength;
}
@@ -56,10 +58,10 @@ public final class UsbACMidiEndpoint extends UsbACEndpoint {
public void report(ReportCanvas canvas) {
super.report(canvas);
- canvas.writeHeader(3, "AC Midi Endpoint: " + ReportCanvas.getHexString(getType())
+ canvas.writeHeader(3, "ACMidi10Endpoint: " + ReportCanvas.getHexString(getType())
+ " Length: " + getLength());
canvas.openList();
canvas.writeListItem("" + getNumJacks() + " Jacks.");
canvas.closeList();
}
-} \ No newline at end of file
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbACMidi20Endpoint.java b/services/usb/java/com/android/server/usb/descriptors/UsbACMidi20Endpoint.java
new file mode 100644
index 000000000000..1024a5bf55a6
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbACMidi20Endpoint.java
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ */
+package com.android.server.usb.descriptors;
+
+import com.android.server.usb.descriptors.report.ReportCanvas;
+
+/**
+ * @hide
+ * An audio class-specific Midi Endpoint.
+ * see midi10.pdf section 6.2.2
+ */
+public final class UsbACMidi20Endpoint extends UsbACEndpoint {
+ private static final String TAG = "UsbACMidi20Endpoint";
+
+ private byte mNumGroupTerminals;
+ private byte[] mBlockIds = new byte[0];
+
+ public UsbACMidi20Endpoint(int length, byte type, int subclass, byte subtype) {
+ super(length, type, subclass, subtype);
+ }
+
+ public byte getNumGroupTerminals() {
+ return mNumGroupTerminals;
+ }
+
+ public byte[] getBlockIds() {
+ return mBlockIds;
+ }
+
+ @Override
+ public int parseRawDescriptors(ByteStream stream) {
+ super.parseRawDescriptors(stream);
+
+ mNumGroupTerminals = stream.getByte();
+ if (mNumGroupTerminals > 0) {
+ mBlockIds = new byte[mNumGroupTerminals];
+ for (int block = 0; block < mNumGroupTerminals; block++) {
+ mBlockIds[block] = stream.getByte();
+ }
+ }
+ return mLength;
+ }
+
+ @Override
+ public void report(ReportCanvas canvas) {
+ super.report(canvas);
+
+ canvas.writeHeader(3, "AC Midi20 Endpoint: " + ReportCanvas.getHexString(getType())
+ + " Length: " + getLength());
+ canvas.openList();
+ canvas.writeListItem("" + getNumGroupTerminals() + " Group Terminals.");
+ canvas.closeList();
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
index 7250a071835d..6e68a9174cb5 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
@@ -30,6 +30,9 @@ public final class UsbDescriptorParser {
private final String mDeviceAddr;
+ private static final int MS_MIDI_1_0 = 0x0100;
+ private static final int MS_MIDI_2_0 = 0x0200;
+
// Descriptor Objects
private static final int DESCRIPTORS_ALLOC_SIZE = 128;
private final ArrayList<UsbDescriptor> mDescriptors;
@@ -215,6 +218,7 @@ public final class UsbDescriptorParser {
Log.w(TAG, " Unparsed Class-specific");
break;
}
+ mCurInterfaceDescriptor.setClassSpecificInterfaceDescriptor(descriptor);
}
break;
@@ -222,17 +226,25 @@ public final class UsbDescriptorParser {
if (mCurInterfaceDescriptor != null) {
int subClass = mCurInterfaceDescriptor.getUsbClass();
switch (subClass) {
- case UsbDescriptor.CLASSID_AUDIO:
- descriptor = UsbACEndpoint.allocDescriptor(this, length, type);
+ case UsbDescriptor.CLASSID_AUDIO: {
+ Byte subType = stream.getByte();
+ if (DEBUG) {
+ Log.d(TAG, "UsbDescriptor.CLASSID_AUDIO type:0x"
+ + Integer.toHexString(type));
+ }
+ descriptor = UsbACEndpoint.allocDescriptor(this, length, type,
+ subType);
+ }
break;
case UsbDescriptor.CLASSID_VIDEO: {
- Byte subtype = stream.getByte();
+ Byte subType = stream.getByte();
if (DEBUG) {
Log.d(TAG, "UsbDescriptor.CLASSID_VIDEO type:0x"
+ Integer.toHexString(type));
}
- descriptor = UsbVCEndpoint.allocDescriptor(this, length, type, subtype);
+ descriptor = UsbVCEndpoint.allocDescriptor(this, length, type,
+ subType);
}
break;
@@ -644,8 +656,8 @@ public final class UsbDescriptorParser {
for (UsbDescriptor descriptor : descriptors) {
// enusure that this isn't an unrecognized interface descriptor
if (descriptor instanceof UsbInterfaceDescriptor) {
- UsbInterfaceDescriptor interfaceDescr = (UsbInterfaceDescriptor) descriptor;
- if (interfaceDescr.getUsbSubclass() == UsbDescriptor.AUDIO_MIDISTREAMING) {
+ UsbInterfaceDescriptor interfaceDescriptor = (UsbInterfaceDescriptor) descriptor;
+ if (interfaceDescriptor.getUsbSubclass() == UsbDescriptor.AUDIO_MIDISTREAMING) {
return true;
}
} else {
@@ -656,17 +668,90 @@ public final class UsbDescriptorParser {
return false;
}
- private int calculateNumMidiPorts(boolean isOutput) {
+ /**
+ * @hide
+ */
+ public boolean containsUniversalMidiDeviceEndpoint() {
+ ArrayList<UsbInterfaceDescriptor> interfaceDescriptors =
+ findUniversalMidiInterfaceDescriptors();
+ int outputCount = 0;
+ int inputCount = 0;
+ for (int interfaceIndex = 0; interfaceIndex < interfaceDescriptors.size();
+ interfaceIndex++) {
+ UsbInterfaceDescriptor interfaceDescriptor = interfaceDescriptors.get(interfaceIndex);
+ for (int endpointIndex = 0; endpointIndex < interfaceDescriptor.getNumEndpoints();
+ endpointIndex++) {
+ UsbEndpointDescriptor endpoint =
+ interfaceDescriptor.getEndpointDescriptor(endpointIndex);
+ // 0 is output, 1 << 7 is input.
+ if (endpoint.getDirection() == 0) {
+ outputCount++;
+ } else {
+ inputCount++;
+ }
+ }
+ }
+ return (outputCount > 0) || (inputCount > 0);
+ }
+
+ /**
+ * @hide
+ */
+ public ArrayList<UsbInterfaceDescriptor> findUniversalMidiInterfaceDescriptors() {
+ int count = 0;
+ ArrayList<UsbDescriptor> descriptors =
+ getInterfaceDescriptorsForClass(UsbDescriptor.CLASSID_AUDIO);
+ ArrayList<UsbInterfaceDescriptor> universalMidiInterfaces =
+ new ArrayList<UsbInterfaceDescriptor>();
+
+ for (UsbDescriptor descriptor : descriptors) {
+ // ensure that this isn't an unrecognized interface descriptor
+ if (descriptor instanceof UsbInterfaceDescriptor) {
+ UsbInterfaceDescriptor interfaceDescriptor = (UsbInterfaceDescriptor) descriptor;
+ if (interfaceDescriptor.getUsbSubclass() == UsbDescriptor.AUDIO_MIDISTREAMING) {
+ UsbDescriptor classSpecificDescriptor =
+ interfaceDescriptor.getClassSpecificInterfaceDescriptor();
+ if (classSpecificDescriptor != null) {
+ if (classSpecificDescriptor instanceof UsbMSMidiHeader) {
+ UsbMSMidiHeader midiHeader =
+ (UsbMSMidiHeader) classSpecificDescriptor;
+ if (midiHeader.getMidiStreamingClass() == MS_MIDI_2_0) {
+ universalMidiInterfaces.add(interfaceDescriptor);
+ }
+ }
+ }
+ }
+ } else {
+ Log.w(TAG, "Undefined Audio Class Interface l: " + descriptor.getLength()
+ + " t:0x" + Integer.toHexString(descriptor.getType()));
+ }
+ }
+ return universalMidiInterfaces;
+ }
+
+ private int calculateNumLegacyMidiPorts(boolean isOutput) {
int count = 0;
ArrayList<UsbDescriptor> descriptors =
getInterfaceDescriptorsForClass(UsbDescriptor.CLASSID_AUDIO);
for (UsbDescriptor descriptor : descriptors) {
// ensure that this isn't an unrecognized interface descriptor
if (descriptor instanceof UsbInterfaceDescriptor) {
- UsbInterfaceDescriptor interfaceDescr = (UsbInterfaceDescriptor) descriptor;
- if (interfaceDescr.getUsbSubclass() == UsbDescriptor.AUDIO_MIDISTREAMING) {
- for (int i = 0; i < interfaceDescr.getNumEndpoints(); i++) {
- UsbEndpointDescriptor endpoint = interfaceDescr.getEndpointDescriptor(i);
+ UsbInterfaceDescriptor interfaceDescriptor = (UsbInterfaceDescriptor) descriptor;
+ if (interfaceDescriptor.getUsbSubclass() == UsbDescriptor.AUDIO_MIDISTREAMING) {
+ UsbDescriptor classSpecificDescriptor =
+ interfaceDescriptor.getClassSpecificInterfaceDescriptor();
+ if (classSpecificDescriptor != null) {
+ if (classSpecificDescriptor instanceof UsbMSMidiHeader) {
+ UsbMSMidiHeader midiHeader =
+ (UsbMSMidiHeader) classSpecificDescriptor;
+ if (midiHeader.getMidiStreamingClass() != MS_MIDI_1_0) {
+ continue;
+ }
+ }
+ }
+ for (int i = 0; i < interfaceDescriptor.getNumEndpoints(); i++) {
+ UsbEndpointDescriptor endpoint =
+ interfaceDescriptor.getEndpointDescriptor(i);
// 0 is output, 1 << 7 is input.
if ((endpoint.getDirection() == 0) == isOutput) {
count++;
@@ -684,15 +769,15 @@ public final class UsbDescriptorParser {
/**
* @hide
*/
- public int calculateNumMidiInputs() {
- return calculateNumMidiPorts(false /*isOutput*/);
+ public int calculateNumLegacyMidiInputs() {
+ return calculateNumLegacyMidiPorts(false /*isOutput*/);
}
/**
* @hide
*/
- public int calculateNumMidiOutputs() {
- return calculateNumMidiPorts(true /*isOutput*/);
+ public int calculateNumLegacyMidiOutputs() {
+ return calculateNumLegacyMidiPorts(true /*isOutput*/);
}
/**
@@ -785,4 +870,35 @@ public final class UsbDescriptorParser {
return getOutputHeadsetProbability() >= OUT_HEADSET_TRIGGER;
}
+ /**
+ * isDock() indicates if the connected USB output peripheral is a docking station with
+ * audio output.
+ * A valid audio dock must declare only one audio output control terminal of type
+ * TERMINAL_EXTERN_DIGITAL.
+ */
+ public boolean isDock() {
+ if (hasMIDIInterface() || hasHIDInterface()) {
+ return false;
+ }
+
+ ArrayList<UsbDescriptor> acDescriptors =
+ getACInterfaceDescriptors(UsbACInterface.ACI_OUTPUT_TERMINAL,
+ UsbACInterface.AUDIO_AUDIOCONTROL);
+
+ if (acDescriptors.size() != 1) {
+ return false;
+ }
+
+ if (acDescriptors.get(0) instanceof UsbACTerminal) {
+ UsbACTerminal outDescr = (UsbACTerminal) acDescriptors.get(0);
+ if (outDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_EXTERN_DIGITAL) {
+ return true;
+ }
+ } else {
+ Log.w(TAG, "Undefined Audio Output terminal l: " + acDescriptors.get(0).getLength()
+ + " t:0x" + Integer.toHexString(acDescriptors.get(0).getType()));
+ }
+ return false;
+ }
+
}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java
index 4d0cfea98630..ab07ce7fdb7a 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java
@@ -112,7 +112,10 @@ public class UsbEndpointDescriptor extends UsbDescriptor {
return mEndpointAddress & UsbEndpointDescriptor.MASK_ENDPOINT_DIRECTION;
}
- /* package */ UsbEndpoint toAndroid(UsbDescriptorParser parser) {
+ /**
+ * Returns a UsbEndpoint that this UsbEndpointDescriptor is describing.
+ */
+ public UsbEndpoint toAndroid(UsbDescriptorParser parser) {
if (UsbDescriptorParser.DEBUG) {
Log.d(TAG, "toAndroid() type:"
+ Integer.toHexString(mAttributes & MASK_ATTRIBS_TRANSTYPE)
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java
index 64dbd971cc67..ca4613b17873 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java
@@ -42,6 +42,8 @@ public class UsbInterfaceDescriptor extends UsbDescriptor {
private ArrayList<UsbEndpointDescriptor> mEndpointDescriptors =
new ArrayList<UsbEndpointDescriptor>();
+ private UsbDescriptor mClassSpecificInterfaceDescriptor;
+
UsbInterfaceDescriptor(int length, byte type) {
super(length, type);
mHierarchyLevel = 3;
@@ -105,7 +107,18 @@ public class UsbInterfaceDescriptor extends UsbDescriptor {
mEndpointDescriptors.add(endpoint);
}
- UsbInterface toAndroid(UsbDescriptorParser parser) {
+ public void setClassSpecificInterfaceDescriptor(UsbDescriptor descriptor) {
+ mClassSpecificInterfaceDescriptor = descriptor;
+ }
+
+ public UsbDescriptor getClassSpecificInterfaceDescriptor() {
+ return mClassSpecificInterfaceDescriptor;
+ }
+
+ /**
+ * Returns a UsbInterface that this UsbInterfaceDescriptor is describing.
+ */
+ public UsbInterface toAndroid(UsbDescriptorParser parser) {
if (UsbDescriptorParser.DEBUG) {
Log.d(TAG, "toAndroid() class:" + Integer.toHexString(mUsbClass)
+ " subclass:" + Integer.toHexString(mUsbSubclass)
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbMSMidiHeader.java b/services/usb/java/com/android/server/usb/descriptors/UsbMSMidiHeader.java
index d0ca6db87d38..76535612f54d 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbMSMidiHeader.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbMSMidiHeader.java
@@ -25,13 +25,19 @@ import com.android.server.usb.descriptors.report.ReportCanvas;
public final class UsbMSMidiHeader extends UsbACInterface {
private static final String TAG = "UsbMSMidiHeader";
+ private int mMidiStreamingClass; // MSC Specification Release (BCD).
+
public UsbMSMidiHeader(int length, byte type, byte subtype, int subclass) {
super(length, type, subtype, subclass);
}
+ public int getMidiStreamingClass() {
+ return mMidiStreamingClass;
+ }
+
@Override
public int parseRawDescriptors(ByteStream stream) {
- // TODO - read data memebers
+ mMidiStreamingClass = stream.unpackUsbShort();
stream.advance(mLength - stream.getReadCount());
return mLength;
}
@@ -42,6 +48,7 @@ public final class UsbMSMidiHeader extends UsbACInterface {
canvas.writeHeader(3, "MS Midi Header: " + ReportCanvas.getHexString(getType())
+ " SubType: " + ReportCanvas.getHexString(getSubclass())
- + " Length: " + getLength());
+ + " Length: " + getLength()
+ + " MidiStreamingClass :" + getMidiStreamingClass());
}
}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbMidiBlockParser.java b/services/usb/java/com/android/server/usb/descriptors/UsbMidiBlockParser.java
new file mode 100644
index 000000000000..37bd0f8f6f7b
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbMidiBlockParser.java
@@ -0,0 +1,173 @@
+/*
+ * 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.
+ */
+package com.android.server.usb.descriptors;
+
+import android.hardware.usb.UsbConstants;
+import android.hardware.usb.UsbDeviceConnection;
+import android.util.Log;
+
+import java.util.ArrayList;
+
+/**
+ * @hide
+ * A class to parse Block Descriptors
+ * see midi20.pdf section 5.4
+ */
+public class UsbMidiBlockParser {
+ private static final String TAG = "UsbMidiBlockParser";
+
+ // Block header size
+ public static final int MIDI_BLOCK_HEADER_SIZE = 5;
+ public static final int MIDI_BLOCK_SIZE = 13;
+ public static final int REQ_GET_DESCRIPTOR = 0x06;
+ public static final int CS_GR_TRM_BLOCK = 0x26; // Class-specific GR_TRM_BLK
+ public static final int GR_TRM_BLOCK_HEADER = 0x01; // Group block header
+ public static final int REQ_TIMEOUT_MS = 2000; // 2 second timeout
+ public static final int DEFAULT_MIDI_TYPE = 1; // Default MIDI type
+
+ protected int mHeaderLength; // 0:1 Size of header descriptor
+ protected int mHeaderDescriptorType; // 1:1 Descriptor Type
+ protected int mHeaderDescriptorSubtype; // 2:1 Descriptor Subtype
+ protected int mTotalLength; // 3:2 Total Length of header and blocks
+
+ static class GroupTerminalBlock {
+ protected int mLength; // 0:1 Size of descriptor
+ protected int mDescriptorType; // 1:1 Descriptor Type
+ protected int mDescriptorSubtype; // 2:1 Descriptor Subtype
+ protected int mGroupBlockId; // 3:1 Id of Group Terminal Block
+ protected int mGroupTerminalBlockType; // 4:1 bi-directional, IN, or OUT
+ protected int mGroupTerminal; // 5:1 Group Terminal Number
+ protected int mNumGroupTerminals; // 6:1 Number of Group Terminals
+ protected int mBlockItem; // 7:1 ID of STRING descriptor of Block item
+ protected int mMidiProtocol; // 8:1 MIDI protocol
+ protected int mMaxInputBandwidth; // 9:2 Max Input Bandwidth
+ protected int mMaxOutputBandwidth; // 11:2 Max Output Bandwidth
+
+ public int parseRawDescriptors(ByteStream stream) {
+ mLength = stream.getUnsignedByte();
+ mDescriptorType = stream.getUnsignedByte();
+ mDescriptorSubtype = stream.getUnsignedByte();
+ mGroupBlockId = stream.getUnsignedByte();
+ mGroupTerminalBlockType = stream.getUnsignedByte();
+ mGroupTerminal = stream.getUnsignedByte();
+ mNumGroupTerminals = stream.getUnsignedByte();
+ mBlockItem = stream.getUnsignedByte();
+ mMidiProtocol = stream.getUnsignedByte();
+ mMaxInputBandwidth = stream.unpackUsbShort();
+ mMaxOutputBandwidth = stream.unpackUsbShort();
+ return mLength;
+ }
+ }
+
+ private ArrayList<GroupTerminalBlock> mGroupTerminalBlocks =
+ new ArrayList<GroupTerminalBlock>();
+
+ public UsbMidiBlockParser() {
+ }
+
+ /**
+ * Parses a raw ByteStream into a block terminal descriptor.
+ * The header is parsed before each block is parsed.
+ * @param stream ByteStream to parse
+ * @return The total length that has been parsed.
+ */
+ public int parseRawDescriptors(ByteStream stream) {
+ mHeaderLength = stream.getUnsignedByte();
+ mHeaderDescriptorType = stream.getUnsignedByte();
+ mHeaderDescriptorSubtype = stream.getUnsignedByte();
+ mTotalLength = stream.unpackUsbShort();
+
+ while (stream.available() >= MIDI_BLOCK_SIZE) {
+ GroupTerminalBlock block = new GroupTerminalBlock();
+ block.parseRawDescriptors(stream);
+ mGroupTerminalBlocks.add(block);
+ }
+
+ return mTotalLength;
+ }
+
+ /**
+ * Calculates the MIDI type through querying the device twice, once for the size
+ * of the block descriptor and once for the block descriptor. This descriptor is
+ * then parsed to return the MIDI type.
+ * See the MIDI 2.0 USB doc for more info.
+ * @param connection UsbDeviceConnection to send the request
+ * @param interfaceNumber The interface number to query
+ * @param alternateInterfaceNumber The alternate interface of the interface
+ * @return The MIDI type as an int.
+ */
+ public int calculateMidiType(UsbDeviceConnection connection, int interfaceNumber,
+ int alternateInterfaceNumber) {
+ byte[] byteArray = new byte[MIDI_BLOCK_HEADER_SIZE];
+ try {
+ // This first request is simply to get the full size of the descriptor.
+ // This info is stored in the last two bytes of the header.
+ int rdo = connection.controlTransfer(
+ UsbConstants.USB_DIR_IN | UsbConstants.USB_TYPE_STANDARD
+ | UsbConstants.USB_CLASS_AUDIO,
+ REQ_GET_DESCRIPTOR,
+ (CS_GR_TRM_BLOCK << 8) + alternateInterfaceNumber,
+ interfaceNumber,
+ byteArray,
+ MIDI_BLOCK_HEADER_SIZE,
+ REQ_TIMEOUT_MS);
+ if (rdo > 0) {
+ if (byteArray[1] != CS_GR_TRM_BLOCK) {
+ Log.e(TAG, "Incorrect descriptor type: " + byteArray[1]);
+ return DEFAULT_MIDI_TYPE;
+ }
+ if (byteArray[2] != GR_TRM_BLOCK_HEADER) {
+ Log.e(TAG, "Incorrect descriptor subtype: " + byteArray[2]);
+ return DEFAULT_MIDI_TYPE;
+ }
+ int newSize = (((int) byteArray[3]) & (0xff))
+ + ((((int) byteArray[4]) & (0xff)) << 8);
+ if (newSize <= 0) {
+ Log.e(TAG, "Parsed a non-positive block terminal size: " + newSize);
+ return DEFAULT_MIDI_TYPE;
+ }
+ byteArray = new byte[newSize];
+ rdo = connection.controlTransfer(
+ UsbConstants.USB_DIR_IN | UsbConstants.USB_TYPE_STANDARD
+ | UsbConstants.USB_CLASS_AUDIO,
+ REQ_GET_DESCRIPTOR,
+ (CS_GR_TRM_BLOCK << 8) + alternateInterfaceNumber,
+ interfaceNumber,
+ byteArray,
+ newSize,
+ REQ_TIMEOUT_MS);
+ if (rdo > 0) {
+ ByteStream stream = new ByteStream(byteArray);
+ parseRawDescriptors(stream);
+ if (mGroupTerminalBlocks.isEmpty()) {
+ Log.e(TAG, "Group Terminal Blocks failed parsing: " + DEFAULT_MIDI_TYPE);
+ return DEFAULT_MIDI_TYPE;
+ } else {
+ Log.d(TAG, "MIDI protocol: " + mGroupTerminalBlocks.get(0).mMidiProtocol);
+ return mGroupTerminalBlocks.get(0).mMidiProtocol;
+ }
+ } else {
+ Log.e(TAG, "second transfer failed: " + rdo);
+ }
+ } else {
+ Log.e(TAG, "first transfer failed: " + rdo);
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Can not communicate with USB device", e);
+ }
+ return DEFAULT_MIDI_TYPE;
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/hal/port/RawPortInfo.java b/services/usb/java/com/android/server/usb/hal/port/RawPortInfo.java
new file mode 100644
index 000000000000..dd256205c220
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/hal/port/RawPortInfo.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.usb.hal.port;
+
+import android.hardware.usb.UsbPortStatus;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Used for storing the raw data from the HAL.
+ * Values of the member variables mocked directly in case of emulation.
+ */
+public final class RawPortInfo implements Parcelable {
+ public final String portId;
+ public final int supportedModes;
+ public final int supportedContaminantProtectionModes;
+ public int currentMode;
+ public boolean canChangeMode;
+ public int currentPowerRole;
+ public boolean canChangePowerRole;
+ public int currentDataRole;
+ public boolean canChangeDataRole;
+ public boolean supportsEnableContaminantPresenceProtection;
+ public int contaminantProtectionStatus;
+ public boolean supportsEnableContaminantPresenceDetection;
+ public int contaminantDetectionStatus;
+ public int[] usbDataStatus;
+ public boolean powerTransferLimited;
+ public int powerBrickStatus;
+
+ public RawPortInfo(String portId, int supportedModes) {
+ this.portId = portId;
+ this.supportedModes = supportedModes;
+ this.supportedContaminantProtectionModes = UsbPortStatus.CONTAMINANT_PROTECTION_NONE;
+ this.supportsEnableContaminantPresenceProtection = false;
+ this.contaminantProtectionStatus = UsbPortStatus.CONTAMINANT_PROTECTION_NONE;
+ this.supportsEnableContaminantPresenceDetection = false;
+ this.contaminantDetectionStatus = UsbPortStatus.CONTAMINANT_DETECTION_NOT_SUPPORTED;
+ this.usbDataStatus[0] = UsbPortStatus.USB_DATA_STATUS_UNKNOWN;
+ this.powerTransferLimited = false;
+ this.powerBrickStatus = UsbPortStatus.POWER_BRICK_STATUS_UNKNOWN;
+ }
+
+ public RawPortInfo(String portId, int supportedModes, int supportedContaminantProtectionModes,
+ int currentMode, boolean canChangeMode,
+ int currentPowerRole, boolean canChangePowerRole,
+ int currentDataRole, boolean canChangeDataRole,
+ boolean supportsEnableContaminantPresenceProtection,
+ int contaminantProtectionStatus,
+ boolean supportsEnableContaminantPresenceDetection,
+ int contaminantDetectionStatus,
+ int[] usbDataStatus,
+ boolean powerTransferLimited,
+ int powerBrickStatus) {
+ this.portId = portId;
+ this.supportedModes = supportedModes;
+ this.supportedContaminantProtectionModes = supportedContaminantProtectionModes;
+ this.currentMode = currentMode;
+ this.canChangeMode = canChangeMode;
+ this.currentPowerRole = currentPowerRole;
+ this.canChangePowerRole = canChangePowerRole;
+ this.currentDataRole = currentDataRole;
+ this.canChangeDataRole = canChangeDataRole;
+ this.supportsEnableContaminantPresenceProtection =
+ supportsEnableContaminantPresenceProtection;
+ this.contaminantProtectionStatus = contaminantProtectionStatus;
+ this.supportsEnableContaminantPresenceDetection =
+ supportsEnableContaminantPresenceDetection;
+ this.contaminantDetectionStatus = contaminantDetectionStatus;
+ this.usbDataStatus = usbDataStatus;
+ this.powerTransferLimited = powerTransferLimited;
+ this.powerBrickStatus = powerBrickStatus;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(portId);
+ dest.writeInt(supportedModes);
+ dest.writeInt(supportedContaminantProtectionModes);
+ dest.writeInt(currentMode);
+ dest.writeByte((byte) (canChangeMode ? 1 : 0));
+ dest.writeInt(currentPowerRole);
+ dest.writeByte((byte) (canChangePowerRole ? 1 : 0));
+ dest.writeInt(currentDataRole);
+ dest.writeByte((byte) (canChangeDataRole ? 1 : 0));
+ dest.writeBoolean(supportsEnableContaminantPresenceProtection);
+ dest.writeInt(contaminantProtectionStatus);
+ dest.writeBoolean(supportsEnableContaminantPresenceDetection);
+ dest.writeInt(contaminantDetectionStatus);
+ dest.writeInt(usbDataStatus.length);
+ dest.writeIntArray(usbDataStatus);
+ dest.writeBoolean(powerTransferLimited);
+ dest.writeInt(powerBrickStatus);
+ }
+
+ public static final Parcelable.Creator<RawPortInfo> CREATOR =
+ new Parcelable.Creator<RawPortInfo>() {
+ @Override
+ public RawPortInfo createFromParcel(Parcel in) {
+ String id = in.readString();
+ int supportedModes = in.readInt();
+ int supportedContaminantProtectionModes = in.readInt();
+ int currentMode = in.readInt();
+ boolean canChangeMode = in.readByte() != 0;
+ int currentPowerRole = in.readInt();
+ boolean canChangePowerRole = in.readByte() != 0;
+ int currentDataRole = in.readInt();
+ boolean canChangeDataRole = in.readByte() != 0;
+ boolean supportsEnableContaminantPresenceProtection = in.readBoolean();
+ int contaminantProtectionStatus = in.readInt();
+ boolean supportsEnableContaminantPresenceDetection = in.readBoolean();
+ int contaminantDetectionStatus = in.readInt();
+ int[] usbDataStatus = new int[in.readInt()];
+ in.readIntArray(usbDataStatus);
+ boolean powerTransferLimited = in.readBoolean();
+ int powerBrickStatus = in.readInt();
+ return new RawPortInfo(id, supportedModes,
+ supportedContaminantProtectionModes, currentMode, canChangeMode,
+ currentPowerRole, canChangePowerRole,
+ currentDataRole, canChangeDataRole,
+ supportsEnableContaminantPresenceProtection,
+ contaminantProtectionStatus,
+ supportsEnableContaminantPresenceDetection,
+ contaminantDetectionStatus, usbDataStatus,
+ powerTransferLimited, powerBrickStatus);
+ }
+
+ @Override
+ public RawPortInfo[] newArray(int size) {
+ return new RawPortInfo[size];
+ }
+ };
+}
diff --git a/services/usb/java/com/android/server/usb/hal/port/UsbPortAidl.java b/services/usb/java/com/android/server/usb/hal/port/UsbPortAidl.java
new file mode 100644
index 000000000000..558260073733
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/hal/port/UsbPortAidl.java
@@ -0,0 +1,609 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.usb.hal.port;
+
+import static android.hardware.usb.UsbManager.USB_HAL_V2_0;
+import static android.hardware.usb.UsbOperationInternal.USB_OPERATION_ERROR_INTERNAL;
+import static android.hardware.usb.UsbOperationInternal.USB_OPERATION_SUCCESS;
+
+import static com.android.server.usb.UsbPortManager.logAndPrint;
+import static com.android.server.usb.UsbPortManager.logAndPrintException;
+
+import android.annotation.Nullable;
+import android.hardware.usb.ContaminantProtectionStatus;
+import android.hardware.usb.IUsb;
+import android.hardware.usb.IUsbOperationInternal;
+import android.hardware.usb.UsbManager.UsbHalVersion;
+import android.hardware.usb.UsbPort;
+import android.hardware.usb.UsbPortStatus;
+import android.hardware.usb.PortMode;
+import android.hardware.usb.Status;
+import android.hardware.usb.IUsbCallback;
+import android.hardware.usb.PortRole;
+import android.hardware.usb.PortStatus;
+import android.os.ServiceManager;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.LongSparseArray;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.usb.UsbPortManager;
+import com.android.server.usb.hal.port.RawPortInfo;
+
+import java.util.ArrayList;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.NoSuchElementException;
+import java.util.Objects;
+
+/**
+ * Implements the methods to interact with AIDL USB HAL.
+ */
+public final class UsbPortAidl implements UsbPortHal {
+ private static final String TAG = UsbPortAidl.class.getSimpleName();
+ private static final String USB_AIDL_SERVICE =
+ "android.hardware.usb.IUsb/default";
+ private static final LongSparseArray<IUsbOperationInternal>
+ sCallbacks = new LongSparseArray<>();
+ // Proxy object for the usb hal daemon.
+ @GuardedBy("mLock")
+ private IUsb mProxy;
+ private UsbPortManager mPortManager;
+ public IndentingPrintWriter mPw;
+ // Mutex for all mutable shared state.
+ private final Object mLock = new Object();
+ // Callback when the UsbPort status is changed by the kernel.
+ private HALCallback mHALCallback;
+ private IBinder mBinder;
+ private boolean mSystemReady;
+ private long mTransactionId;
+
+ public @UsbHalVersion int getUsbHalVersion() throws RemoteException {
+ synchronized (mLock) {
+ if (mProxy == null) {
+ throw new RemoteException("IUsb not initialized yet");
+ }
+ }
+ logAndPrint(Log.INFO, null, "USB HAL AIDL version: USB_HAL_V2_0");
+ return USB_HAL_V2_0;
+ }
+
+ @Override
+ public void systemReady() {
+ mSystemReady = true;
+ }
+
+ public void serviceDied() {
+ logAndPrint(Log.ERROR, mPw, "Usb AIDL hal service died");
+ synchronized (mLock) {
+ mProxy = null;
+ }
+ connectToProxy(null);
+ }
+
+ private void connectToProxy(IndentingPrintWriter pw) {
+ synchronized (mLock) {
+ if (mProxy != null) {
+ return;
+ }
+
+ try {
+ mBinder = ServiceManager.waitForService(USB_AIDL_SERVICE);
+ mProxy = IUsb.Stub.asInterface(mBinder);
+ mBinder.linkToDeath(this::serviceDied, 0);
+ mProxy.setCallback(mHALCallback);
+ mProxy.queryPortStatus(++mTransactionId);
+ } catch (NoSuchElementException e) {
+ logAndPrintException(pw, "connectToProxy: usb hal service not found."
+ + " Did the service fail to start?", e);
+ } catch (RemoteException e) {
+ logAndPrintException(pw, "connectToProxy: usb hal service not responding", e);
+ }
+ }
+ }
+
+ static boolean isServicePresent(IndentingPrintWriter pw) {
+ try {
+ return ServiceManager.isDeclared(USB_AIDL_SERVICE);
+ } catch (NoSuchElementException e) {
+ logAndPrintException(pw, "connectToProxy: usb Aidl hal service not found.", e);
+ }
+
+ return false;
+ }
+
+ public UsbPortAidl(UsbPortManager portManager, IndentingPrintWriter pw) {
+ mPortManager = Objects.requireNonNull(portManager);
+ mPw = pw;
+ mHALCallback = new HALCallback(null, mPortManager, this);
+ connectToProxy(mPw);
+ }
+
+ @Override
+ public void enableContaminantPresenceDetection(String portName, boolean enable,
+ long operationID) {
+ synchronized (mLock) {
+ if (mProxy == null) {
+ logAndPrint(Log.ERROR, mPw, "Proxy is null. Retry ! opID: "
+ + operationID);
+ return;
+ }
+
+ try {
+ // Oneway call into the hal. Use the castFrom method from HIDL.
+ mProxy.enableContaminantPresenceDetection(portName, enable, operationID);
+ } catch (RemoteException e) {
+ logAndPrintException(mPw, "Failed to set contaminant detection. opID:"
+ + operationID, e);
+ }
+ }
+ }
+
+ @Override
+ public void queryPortStatus(long operationID) {
+ synchronized (mLock) {
+ if (mProxy == null) {
+ logAndPrint(Log.ERROR, mPw, "Proxy is null. Retry ! opID:"
+ + operationID);
+ return;
+ }
+
+ try {
+ mProxy.queryPortStatus(operationID);
+ } catch (RemoteException e) {
+ logAndPrintException(null, "ServiceStart: Failed to query port status. opID:"
+ + operationID, e);
+ }
+ }
+ }
+
+ @Override
+ public void switchMode(String portId, @HalUsbPortMode int newMode, long operationID) {
+ synchronized (mLock) {
+ if (mProxy == null) {
+ logAndPrint(Log.ERROR, mPw, "Proxy is null. Retry ! opID:"
+ + operationID);
+ return;
+ }
+
+ PortRole newRole = new PortRole();
+ newRole.setMode((byte)newMode);
+ try {
+ mProxy.switchRole(portId, newRole, operationID);
+ } catch (RemoteException e) {
+ logAndPrintException(mPw, "Failed to set the USB port mode: "
+ + "portId=" + portId
+ + ", newMode=" + UsbPort.modeToString(newMode)
+ + "opID:" + operationID, e);
+ }
+ }
+ }
+
+ @Override
+ public void switchPowerRole(String portId, @HalUsbPowerRole int newPowerRole,
+ long operationID) {
+ synchronized (mLock) {
+ if (mProxy == null) {
+ logAndPrint(Log.ERROR, mPw, "Proxy is null. Retry ! opID:"
+ + operationID);
+ return;
+ }
+
+ PortRole newRole = new PortRole();
+ newRole.setPowerRole((byte)newPowerRole);
+ try {
+ mProxy.switchRole(portId, newRole, operationID);
+ } catch (RemoteException e) {
+ logAndPrintException(mPw, "Failed to set the USB power role: portId=" + portId
+ + ", newPowerRole=" + UsbPort.powerRoleToString(newPowerRole)
+ + "opID:" + operationID, e);
+ }
+ }
+ }
+
+ @Override
+ public void switchDataRole(String portId, @HalUsbDataRole int newDataRole, long operationID) {
+ synchronized (mLock) {
+ if (mProxy == null) {
+ logAndPrint(Log.ERROR, mPw, "Proxy is null. Retry ! opID:"
+ + operationID);
+ return;
+ }
+
+ PortRole newRole = new PortRole();
+ newRole.setDataRole((byte)newDataRole);
+ try {
+ mProxy.switchRole(portId, newRole, operationID);
+ } catch (RemoteException e) {
+ logAndPrintException(mPw, "Failed to set the USB data role: portId=" + portId
+ + ", newDataRole=" + UsbPort.dataRoleToString(newDataRole)
+ + "opID:" + operationID, e);
+ }
+ }
+ }
+
+ @Override
+ public boolean enableUsbData(String portName, boolean enable, long operationID,
+ IUsbOperationInternal callback) {
+ Objects.requireNonNull(portName);
+ Objects.requireNonNull(callback);
+ long key = operationID;
+ synchronized (mLock) {
+ try {
+ if (mProxy == null) {
+ logAndPrint(Log.ERROR, mPw,
+ "enableUsbData: Proxy is null. Retry !opID:"
+ + operationID);
+ callback.onOperationComplete(USB_OPERATION_ERROR_INTERNAL);
+ return false;
+ }
+ while (sCallbacks.get(key) != null) {
+ key = ThreadLocalRandom.current().nextInt();
+ }
+ if (key != operationID) {
+ logAndPrint(Log.INFO, mPw, "enableUsbData: operationID exists ! opID:"
+ + operationID + " key:" + key);
+ }
+ try {
+ sCallbacks.put(key, callback);
+ mProxy.enableUsbData(portName, enable, key);
+ } catch (RemoteException e) {
+ logAndPrintException(mPw,
+ "enableUsbData: Failed to invoke enableUsbData: portID="
+ + portName + "opID:" + operationID, e);
+ callback.onOperationComplete(USB_OPERATION_ERROR_INTERNAL);
+ sCallbacks.remove(key);
+ return false;
+ }
+ } catch (RemoteException e) {
+ logAndPrintException(mPw,
+ "enableUsbData: Failed to call onOperationComplete portID="
+ + portName + "opID:" + operationID, e);
+ sCallbacks.remove(key);
+ return false;
+ }
+ return true;
+ }
+ }
+
+ @Override
+ public void enableLimitPowerTransfer(String portName, boolean limit, long operationID,
+ IUsbOperationInternal callback) {
+ Objects.requireNonNull(portName);
+ long key = operationID;
+ synchronized (mLock) {
+ try {
+ if (mProxy == null) {
+ logAndPrint(Log.ERROR, mPw,
+ "enableLimitPowerTransfer: Proxy is null. Retry !opID:"
+ + operationID);
+ callback.onOperationComplete(USB_OPERATION_ERROR_INTERNAL);
+ return;
+ }
+ while (sCallbacks.get(key) != null) {
+ key = ThreadLocalRandom.current().nextInt();
+ }
+ if (key != operationID) {
+ logAndPrint(Log.INFO, mPw, "enableUsbData: operationID exists ! opID:"
+ + operationID + " key:" + key);
+ }
+ try {
+ sCallbacks.put(key, callback);
+ mProxy.limitPowerTransfer(portName, limit, key);
+ } catch (RemoteException e) {
+ logAndPrintException(mPw,
+ "enableLimitPowerTransfer: Failed while invoking AIDL HAL"
+ + " portID=" + portName + " opID:" + operationID, e);
+ if (callback != null) {
+ callback.onOperationComplete(USB_OPERATION_ERROR_INTERNAL);
+ }
+ sCallbacks.remove(key);
+ }
+ } catch (RemoteException e) {
+ logAndPrintException(mPw,
+ "enableLimitPowerTransfer: Failed to call onOperationComplete portID="
+ + portName + " opID:" + operationID, e);
+ }
+ }
+ }
+
+ @Override
+ public void enableUsbDataWhileDocked(String portName, long operationID,
+ IUsbOperationInternal callback) {
+ Objects.requireNonNull(portName);
+ long key = operationID;
+ synchronized (mLock) {
+ try {
+ if (mProxy == null) {
+ logAndPrint(Log.ERROR, mPw,
+ "enableUsbDataWhileDocked: Proxy is null. Retry !opID:"
+ + operationID);
+ callback.onOperationComplete(USB_OPERATION_ERROR_INTERNAL);
+ return;
+ }
+ while (sCallbacks.get(key) != null) {
+ key = ThreadLocalRandom.current().nextInt();
+ }
+ if (key != operationID) {
+ logAndPrint(Log.INFO, mPw,
+ "enableUsbDataWhileDocked: operationID exists ! opID:"
+ + operationID + " key:" + key);
+ }
+ try {
+ sCallbacks.put(key, callback);
+ mProxy.enableUsbDataWhileDocked(portName, key);
+ } catch (RemoteException e) {
+ logAndPrintException(mPw,
+ "enableUsbDataWhileDocked: error while invoking hal"
+ + "portID=" + portName + " opID:" + operationID, e);
+ if (callback != null) {
+ callback.onOperationComplete(USB_OPERATION_ERROR_INTERNAL);
+ }
+ sCallbacks.remove(key);
+ }
+ } catch (RemoteException e) {
+ logAndPrintException(mPw,
+ "enableUsbDataWhileDocked: Failed to call onOperationComplete portID="
+ + portName + " opID:" + operationID, e);
+ }
+ }
+ }
+
+ private static class HALCallback extends IUsbCallback.Stub {
+ public IndentingPrintWriter mPw;
+ public UsbPortManager mPortManager;
+ public UsbPortAidl mUsbPortAidl;
+
+ HALCallback(IndentingPrintWriter pw, UsbPortManager portManager, UsbPortAidl usbPortAidl) {
+ this.mPw = pw;
+ this.mPortManager = portManager;
+ this.mUsbPortAidl = usbPortAidl;
+ }
+
+ /**
+ * Converts from AIDL defined mode constants to UsbPortStatus constants.
+ * AIDL does not gracefully support bitfield when combined with enums.
+ */
+ private int toPortMode(byte aidlPortMode) {
+ switch (aidlPortMode) {
+ case PortMode.NONE:
+ return UsbPortStatus.MODE_NONE;
+ case PortMode.UFP:
+ return UsbPortStatus.MODE_UFP;
+ case PortMode.DFP:
+ return UsbPortStatus.MODE_DFP;
+ case PortMode.DRP:
+ return UsbPortStatus.MODE_DUAL;
+ case PortMode.AUDIO_ACCESSORY:
+ return UsbPortStatus.MODE_AUDIO_ACCESSORY;
+ case PortMode.DEBUG_ACCESSORY:
+ return UsbPortStatus.MODE_DEBUG_ACCESSORY;
+ default:
+ UsbPortManager.logAndPrint(Log.ERROR, mPw, "Unrecognized aidlPortMode:"
+ + aidlPortMode);
+ return UsbPortStatus.MODE_NONE;
+ }
+ }
+
+ private int toSupportedModes(byte[] aidlPortModes) {
+ int supportedModes = UsbPortStatus.MODE_NONE;
+
+ for (byte aidlPortMode : aidlPortModes) {
+ supportedModes |= toPortMode(aidlPortMode);
+ }
+
+ return supportedModes;
+ }
+
+ /**
+ * Converts from AIDL defined contaminant protection constants to UsbPortStatus constants.
+ * AIDL does not gracefully support bitfield when combined with enums.
+ * Common to both ContaminantProtectionMode and ContaminantProtectionStatus.
+ */
+ private int toContaminantProtectionStatus(byte aidlContaminantProtection) {
+ switch (aidlContaminantProtection) {
+ case ContaminantProtectionStatus.NONE:
+ return UsbPortStatus.CONTAMINANT_PROTECTION_NONE;
+ case ContaminantProtectionStatus.FORCE_SINK:
+ return UsbPortStatus.CONTAMINANT_PROTECTION_SINK;
+ case ContaminantProtectionStatus.FORCE_SOURCE:
+ return UsbPortStatus.CONTAMINANT_PROTECTION_SOURCE;
+ case ContaminantProtectionStatus.FORCE_DISABLE:
+ return UsbPortStatus.CONTAMINANT_PROTECTION_FORCE_DISABLE;
+ case ContaminantProtectionStatus.DISABLED:
+ return UsbPortStatus.CONTAMINANT_PROTECTION_DISABLED;
+ default:
+ UsbPortManager.logAndPrint(Log.ERROR, mPw,
+ "Unrecognized aidlContaminantProtection:"
+ + aidlContaminantProtection);
+ return UsbPortStatus.CONTAMINANT_PROTECTION_NONE;
+ }
+ }
+
+ private int toSupportedContaminantProtectionModes(byte[] aidlModes) {
+ int supportedContaminantProtectionModes = UsbPortStatus.CONTAMINANT_PROTECTION_NONE;
+
+ for (byte aidlMode : aidlModes) {
+ supportedContaminantProtectionModes |= toContaminantProtectionStatus(aidlMode);
+ }
+
+ return supportedContaminantProtectionModes;
+ }
+
+ private int[] toIntArray(byte[] input) {
+ int[] output = new int[input.length];
+ for (int i = 0; i < input.length; i++) {
+ output[i] = input[i];
+ }
+ return output;
+ }
+
+ @Override
+ public void notifyPortStatusChange(
+ android.hardware.usb.PortStatus[] currentPortStatus, int retval) {
+ if (!mUsbPortAidl.mSystemReady) {
+ return;
+ }
+
+ if (retval != Status.SUCCESS) {
+ UsbPortManager.logAndPrint(Log.ERROR, mPw, "port status enquiry failed");
+ return;
+ }
+
+ ArrayList<RawPortInfo> newPortInfo = new ArrayList<>();
+
+ int numStatus = currentPortStatus.length;
+ for (int i = 0; i < numStatus; i++) {
+ PortStatus current = currentPortStatus[i];
+ RawPortInfo temp = new RawPortInfo(current.portName,
+ toSupportedModes(current.supportedModes),
+ toSupportedContaminantProtectionModes(current
+ .supportedContaminantProtectionModes),
+ toPortMode(current.currentMode),
+ current.canChangeMode,
+ current.currentPowerRole,
+ current.canChangePowerRole,
+ current.currentDataRole,
+ current.canChangeDataRole,
+ current.supportsEnableContaminantPresenceProtection,
+ toContaminantProtectionStatus(current.contaminantProtectionStatus),
+ current.supportsEnableContaminantPresenceDetection,
+ current.contaminantDetectionStatus,
+ toIntArray(current.usbDataStatus),
+ current.powerTransferLimited,
+ current.powerBrickStatus);
+ newPortInfo.add(temp);
+ UsbPortManager.logAndPrint(Log.INFO, mPw, "ClientCallback AIDL V1: "
+ + current.portName);
+ }
+ mPortManager.updatePorts(newPortInfo);
+ }
+
+ @Override
+ public void notifyRoleSwitchStatus(String portName, PortRole role, int retval,
+ long operationID) {
+ if (retval == Status.SUCCESS) {
+ UsbPortManager.logAndPrint(Log.INFO, mPw, portName
+ + " role switch successful. opID:"
+ + operationID);
+ } else {
+ UsbPortManager.logAndPrint(Log.ERROR, mPw, portName + " role switch failed. err:"
+ + retval
+ + "opID:" + operationID);
+ }
+ }
+
+ @Override
+ public void notifyQueryPortStatus(String portName, int retval, long operationID) {
+ if (retval == Status.SUCCESS) {
+ UsbPortManager.logAndPrint(Log.INFO, mPw, portName + ": opID:"
+ + operationID + " successful");
+ } else {
+ UsbPortManager.logAndPrint(Log.ERROR, mPw, portName + ": opID:"
+ + operationID + " failed. err:" + retval);
+ }
+ }
+
+ @Override
+ public void notifyEnableUsbDataStatus(String portName, boolean enable, int retval,
+ long operationID) {
+ if (retval == Status.SUCCESS) {
+ UsbPortManager.logAndPrint(Log.INFO, mPw, "notifyEnableUsbDataStatus:"
+ + portName + ": opID:"
+ + operationID + " enable:" + enable);
+ } else {
+ UsbPortManager.logAndPrint(Log.ERROR, mPw, portName
+ + "notifyEnableUsbDataStatus: opID:"
+ + operationID + " failed. err:" + retval);
+ }
+ try {
+ sCallbacks.get(operationID).onOperationComplete(retval == Status.SUCCESS
+ ? USB_OPERATION_SUCCESS
+ : USB_OPERATION_ERROR_INTERNAL);
+ } catch (RemoteException e) {
+ logAndPrintException(mPw,
+ "notifyEnableUsbDataStatus: Failed to call onOperationComplete",
+ e);
+ }
+ }
+
+ @Override
+ public void notifyContaminantEnabledStatus(String portName, boolean enable, int retval,
+ long operationID) {
+ if (retval == Status.SUCCESS) {
+ UsbPortManager.logAndPrint(Log.INFO, mPw, "notifyContaminantEnabledStatus:"
+ + portName + ": opID:"
+ + operationID + " enable:" + enable);
+ } else {
+ UsbPortManager.logAndPrint(Log.ERROR, mPw, portName
+ + "notifyContaminantEnabledStatus: opID:"
+ + operationID + " failed. err:" + retval);
+ }
+ }
+
+ @Override
+ public void notifyLimitPowerTransferStatus(String portName, boolean limit, int retval,
+ long operationID) {
+ if (retval == Status.SUCCESS) {
+ UsbPortManager.logAndPrint(Log.INFO, mPw, portName + ": opID:"
+ + operationID + " successful");
+ } else {
+ UsbPortManager.logAndPrint(Log.ERROR, mPw, portName
+ + "notifyLimitPowerTransferStatus: opID:"
+ + operationID + " failed. err:" + retval);
+ }
+ try {
+ IUsbOperationInternal callback = sCallbacks.get(operationID);
+ if (callback != null) {
+ sCallbacks.get(operationID).onOperationComplete(retval == Status.SUCCESS
+ ? USB_OPERATION_SUCCESS
+ : USB_OPERATION_ERROR_INTERNAL);
+ }
+ } catch (RemoteException e) {
+ logAndPrintException(mPw,
+ "enableLimitPowerTransfer: Failed to call onOperationComplete",
+ e);
+ }
+ }
+
+ @Override
+ public void notifyEnableUsbDataWhileDockedStatus(String portName, int retval,
+ long operationID) {
+ if (retval == Status.SUCCESS) {
+ UsbPortManager.logAndPrint(Log.INFO, mPw, portName + ": opID:"
+ + operationID + " successful");
+ } else {
+ UsbPortManager.logAndPrint(Log.ERROR, mPw, portName
+ + "notifyEnableUsbDataWhileDockedStatus: opID:"
+ + operationID + " failed. err:" + retval);
+ }
+ try {
+ IUsbOperationInternal callback = sCallbacks.get(operationID);
+ if (callback != null) {
+ sCallbacks.get(operationID).onOperationComplete(retval == Status.SUCCESS
+ ? USB_OPERATION_SUCCESS
+ : USB_OPERATION_ERROR_INTERNAL);
+ }
+ } catch (RemoteException e) {
+ logAndPrintException(mPw,
+ "notifyEnableUsbDataWhileDockedStatus: Failed to call onOperationComplete",
+ e);
+ }
+ }
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/hal/port/UsbPortHal.java b/services/usb/java/com/android/server/usb/hal/port/UsbPortHal.java
new file mode 100644
index 000000000000..abfdd6f517cd
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/hal/port/UsbPortHal.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.usb.hal.port;
+
+import android.annotation.IntDef;
+import android.hardware.usb.IUsbOperationInternal;
+import android.hardware.usb.UsbManager.UsbHalVersion;
+import android.os.RemoteException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.String;
+
+/**
+ * @hide
+ */
+public interface UsbPortHal {
+ /**
+ * Power role: This USB port can act as a source (provide power).
+ * @hide
+ */
+ public static final int HAL_POWER_ROLE_SOURCE = 1;
+
+ /**
+ * Power role: This USB port can act as a sink (receive power).
+ * @hide
+ */
+ public static final int HAL_POWER_ROLE_SINK = 2;
+
+ @IntDef(prefix = { "HAL_POWER_ROLE_" }, value = {
+ HAL_POWER_ROLE_SOURCE,
+ HAL_POWER_ROLE_SINK
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface HalUsbPowerRole{}
+
+ /**
+ * Data role: This USB port can act as a host (access data services).
+ * @hide
+ */
+ public static final int HAL_DATA_ROLE_HOST = 1;
+
+ /**
+ * Data role: This USB port can act as a device (offer data services).
+ * @hide
+ */
+ public static final int HAL_DATA_ROLE_DEVICE = 2;
+
+ @IntDef(prefix = { "HAL_DATA_ROLE_" }, value = {
+ HAL_DATA_ROLE_HOST,
+ HAL_DATA_ROLE_DEVICE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface HalUsbDataRole{}
+
+ /**
+ * This USB port can act as a downstream facing port (host).
+ *
+ * @hide
+ */
+ public static final int HAL_MODE_DFP = 1;
+
+ /**
+ * This USB port can act as an upstream facing port (device).
+ *
+ * @hide
+ */
+ public static final int HAL_MODE_UFP = 2;
+ @IntDef(prefix = { "HAL_MODE_" }, value = {
+ HAL_MODE_DFP,
+ HAL_MODE_UFP,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface HalUsbPortMode{}
+
+ /**
+ * UsbPortManager would call this when the system is done booting.
+ */
+ public void systemReady();
+
+ /**
+ * Invoked to enable/disable contaminant presence detection on the USB port.
+ *
+ * @param portName Port Identifier.
+ * @param enable Enable contaminant presence detection when true.
+ * Disable when false.
+ * @param transactionId Used for tracking the current request and is passed down to the HAL
+ * implementation as needed.
+ */
+ public void enableContaminantPresenceDetection(String portName, boolean enable,
+ long transactionId);
+
+ /**
+ * Invoked to query port status of all the ports.
+ *
+ * @param transactionId Used for tracking the current request and is passed down to the HAL
+ * implementation as needed.
+ */
+ public void queryPortStatus(long transactionId);
+
+ /**
+ * Invoked to switch USB port mode.
+ *
+ * @param portName Port Identifier.
+ * @param mode New mode that the port is switching into.
+ * @param transactionId Used for tracking the current request and is passed down to the HAL
+ * implementation as needed.
+ */
+ public void switchMode(String portName, @HalUsbPortMode int mode, long transactionId);
+
+ /**
+ * Invoked to switch USB port power role.
+ *
+ * @param portName Port Identifier.
+ * @param powerRole New power role that the port is switching into.
+ * @param transactionId Used for tracking the current request and is passed down to the HAL
+ * implementation as needed.
+ */
+ public void switchPowerRole(String portName, @HalUsbPowerRole int powerRole,
+ long transactionId);
+
+ /**
+ * Invoked to switch USB port data role.
+ *
+ * @param portName Port Identifier.
+ * @param dataRole New data role that the port is switching into.
+ * @param transactionId Used for tracking the current request and is passed down to the HAL
+ * implementation as needed.
+ */
+ public void switchDataRole(String portName, @HalUsbDataRole int dataRole, long transactionId);
+
+ /**
+ * Invoked to query the version of current hal implementation.
+ */
+ public @UsbHalVersion int getUsbHalVersion() throws RemoteException;
+
+ /**
+ * Invoked to enable/disable UsbData on the specified port.
+ *
+ * @param portName Port Identifier.
+ * @param enable Enable USB data when true.
+ * Disable when false.
+ * @param transactionId Used for tracking the current request and is passed down to the HAL
+ * implementation as needed.
+ * @param callback callback object to be invoked to invoke the status of the operation upon
+ * completion.
+ * @param callback callback object to be invoked when the operation is complete.
+ * @return True when the operation is asynchronous. The caller of
+ * {@link UsbOperationInternal} must therefore call
+ * {@link UsbOperationInternal#waitForOperationComplete} for processing
+ * the result.
+ * False when the operation is synchronous. Caller can proceed reading the result
+ * through {@link UsbOperationInternal#getStatus}
+ */
+ public boolean enableUsbData(String portName, boolean enable, long transactionId,
+ IUsbOperationInternal callback);
+
+ /**
+ * Invoked to enable UsbData when disabled due to docking event.
+ *
+ * @param portName Port Identifier.
+ * @param transactionId Used for tracking the current request and is passed down to the HAL
+ * implementation as needed.
+ * @param callback callback object to be invoked to invoke the status of the operation upon
+ * completion.
+ */
+ public void enableUsbDataWhileDocked(String portName, long transactionId,
+ IUsbOperationInternal callback);
+
+ /**
+ * Invoked to enableLimitPowerTransfer on the specified port.
+ *
+ * @param portName Port Identifier.
+ * @param limit limit power transfer when true. Port wouldn't charge or power USB accessoried
+ * when set.
+ * Lift power transfer restrictions when false.
+ * @param transactionId Used for tracking the current request and is passed down to the HAL
+ * implementation as needed.
+ * @param callback callback object to be invoked to invoke the status of the operation upon
+ * completion.
+ */
+ public void enableLimitPowerTransfer(String portName, boolean limit, long transactionId,
+ IUsbOperationInternal callback);
+}
diff --git a/services/usb/java/com/android/server/usb/hal/port/UsbPortHalInstance.java b/services/usb/java/com/android/server/usb/hal/port/UsbPortHalInstance.java
new file mode 100644
index 000000000000..41f9faef99df
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/hal/port/UsbPortHalInstance.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.usb.hal.port;
+
+import static com.android.server.usb.UsbPortManager.logAndPrint;
+
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.usb.hal.port.UsbPortHidl;
+import com.android.server.usb.hal.port.UsbPortAidl;
+import com.android.server.usb.UsbPortManager;
+
+import android.util.Log;
+/**
+ * Helper class that queries the underlying hal layer to populate UsbPortHal instance.
+ */
+public final class UsbPortHalInstance {
+
+ public static UsbPortHal getInstance(UsbPortManager portManager, IndentingPrintWriter pw) {
+
+ logAndPrint(Log.DEBUG, null, "Querying USB HAL version");
+ if (UsbPortHidl.isServicePresent(null)) {
+ logAndPrint(Log.INFO, null, "USB HAL HIDL present");
+ return new UsbPortHidl(portManager, pw);
+ }
+ if (UsbPortAidl.isServicePresent(null)) {
+ logAndPrint(Log.INFO, null, "USB HAL AIDL present");
+ return new UsbPortAidl(portManager, pw);
+ }
+
+ return null;
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/hal/port/UsbPortHidl.java b/services/usb/java/com/android/server/usb/hal/port/UsbPortHidl.java
new file mode 100644
index 000000000000..c1d76355e75b
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/hal/port/UsbPortHidl.java
@@ -0,0 +1,500 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.usb.hal.port;
+
+import static android.hardware.usb.UsbManager.USB_HAL_NOT_SUPPORTED;
+import static android.hardware.usb.UsbManager.USB_HAL_V1_0;
+import static android.hardware.usb.UsbManager.USB_HAL_V1_1;
+import static android.hardware.usb.UsbManager.USB_HAL_V1_2;
+import static android.hardware.usb.UsbManager.USB_HAL_V1_3;
+import static android.hardware.usb.UsbOperationInternal.USB_OPERATION_ERROR_INTERNAL;
+import static android.hardware.usb.UsbOperationInternal.USB_OPERATION_ERROR_NOT_SUPPORTED;
+import static android.hardware.usb.UsbOperationInternal.USB_OPERATION_SUCCESS;
+import static android.hardware.usb.UsbPortStatus.CONTAMINANT_DETECTION_NOT_SUPPORTED;
+import static android.hardware.usb.UsbPortStatus.CONTAMINANT_PROTECTION_NONE;
+import static android.hardware.usb.UsbPortStatus.DATA_ROLE_DEVICE;
+import static android.hardware.usb.UsbPortStatus.DATA_ROLE_HOST;
+import static android.hardware.usb.UsbPortStatus.MODE_DFP;
+import static android.hardware.usb.UsbPortStatus.MODE_DUAL;
+import static android.hardware.usb.UsbPortStatus.MODE_UFP;
+import static android.hardware.usb.UsbPortStatus.POWER_BRICK_STATUS_UNKNOWN;
+import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SINK;
+import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SOURCE;
+import static android.hardware.usb.UsbPortStatus.USB_DATA_STATUS_DISABLED_FORCE;
+import static android.hardware.usb.UsbPortStatus.USB_DATA_STATUS_UNKNOWN;
+
+
+import static com.android.server.usb.UsbPortManager.logAndPrint;
+import static com.android.server.usb.UsbPortManager.logAndPrintException;
+
+import android.annotation.Nullable;
+import android.hardware.usb.IUsbOperationInternal;
+import android.hardware.usb.UsbManager.UsbHalVersion;
+import android.hardware.usb.UsbPort;
+import android.hardware.usb.UsbPortStatus;
+import android.hardware.usb.V1_0.IUsb;
+import android.hardware.usb.V1_0.PortRoleType;
+import android.hardware.usb.V1_0.Status;
+import android.hardware.usb.V1_1.PortStatus_1_1;
+import android.hardware.usb.V1_2.IUsbCallback;
+import android.hardware.usb.V1_0.PortRole;
+import android.hardware.usb.V1_2.PortStatus;
+import android.hidl.manager.V1_0.IServiceManager;
+import android.hidl.manager.V1_0.IServiceNotification;
+import android.os.IHwBinder;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.usb.UsbPortManager;
+import com.android.server.usb.hal.port.RawPortInfo;
+
+import java.util.ArrayList;
+import java.util.NoSuchElementException;
+import java.util.Objects;
+/**
+ *
+ */
+public final class UsbPortHidl implements UsbPortHal {
+ private static final String TAG = UsbPortHidl.class.getSimpleName();
+ // Cookie sent for usb hal death notification.
+ private static final int USB_HAL_DEATH_COOKIE = 1000;
+ // Proxy object for the usb hal daemon.
+ @GuardedBy("mLock")
+ private IUsb mProxy;
+ private UsbPortManager mPortManager;
+ public IndentingPrintWriter mPw;
+ // Mutex for all mutable shared state.
+ private final Object mLock = new Object();
+ // Callback when the UsbPort status is changed by the kernel.
+ private HALCallback mHALCallback;
+ private boolean mSystemReady;
+ // Workaround since HIDL HAL versions report UsbDataEnabled status in UsbPortStatus;
+ private static int sUsbDataStatus = USB_DATA_STATUS_UNKNOWN;
+
+ public @UsbHalVersion int getUsbHalVersion() throws RemoteException {
+ int version;
+ synchronized(mLock) {
+ if (mProxy == null) {
+ throw new RemoteException("IUsb not initialized yet");
+ }
+ if (android.hardware.usb.V1_3.IUsb.castFrom(mProxy) != null) {
+ version = USB_HAL_V1_3;
+ } else if (android.hardware.usb.V1_2.IUsb.castFrom(mProxy) != null) {
+ version = USB_HAL_V1_2;
+ } else if (android.hardware.usb.V1_1.IUsb.castFrom(mProxy) != null) {
+ version = USB_HAL_V1_1;
+ } else {
+ version = USB_HAL_V1_0;
+ }
+ logAndPrint(Log.INFO, null, "USB HAL HIDL version: " + version);
+ return version;
+ }
+ }
+
+ final class DeathRecipient implements IHwBinder.DeathRecipient {
+ public IndentingPrintWriter pw;
+
+ DeathRecipient(IndentingPrintWriter pw) {
+ this.pw = pw;
+ }
+
+ @Override
+ public void serviceDied(long cookie) {
+ if (cookie == USB_HAL_DEATH_COOKIE) {
+ logAndPrint(Log.ERROR, pw, "Usb hal service died cookie: " + cookie);
+ synchronized (mLock) {
+ mProxy = null;
+ }
+ }
+ }
+ }
+
+ final class ServiceNotification extends IServiceNotification.Stub {
+ @Override
+ public void onRegistration(String fqName, String name, boolean preexisting) {
+ logAndPrint(Log.INFO, null, "Usb hal service started " + fqName + " " + name);
+ connectToProxy(null);
+ }
+ }
+
+ private void connectToProxy(IndentingPrintWriter pw) {
+ synchronized (mLock) {
+ if (mProxy != null) {
+ return;
+ }
+
+ try {
+ mProxy = IUsb.getService();
+ mProxy.linkToDeath(new DeathRecipient(pw), USB_HAL_DEATH_COOKIE);
+ mProxy.setCallback(mHALCallback);
+ mProxy.queryPortStatus();
+ //updateUsbHalVersion();
+ } catch (NoSuchElementException e) {
+ logAndPrintException(pw, "connectToProxy: usb hal service not found."
+ + " Did the service fail to start?", e);
+ } catch (RemoteException e) {
+ logAndPrintException(pw, "connectToProxy: usb hal service not responding", e);
+ }
+ }
+ }
+
+ @Override
+ public void systemReady() {
+ mSystemReady = true;
+ }
+
+ static boolean isServicePresent(IndentingPrintWriter pw) {
+ try {
+ IUsb.getService(true);
+ } catch (NoSuchElementException e) {
+ logAndPrintException(pw, "connectToProxy: usb hidl hal service not found.", e);
+ return false;
+ } catch (RemoteException e) {
+ logAndPrintException(pw, "IUSB hal service present but failed to get service", e);
+ }
+
+ return true;
+ }
+
+ public UsbPortHidl(UsbPortManager portManager, IndentingPrintWriter pw) {
+ mPortManager = Objects.requireNonNull(portManager);
+ mPw = pw;
+ mHALCallback = new HALCallback(null, mPortManager, this);
+ try {
+ ServiceNotification serviceNotification = new ServiceNotification();
+
+ boolean ret = IServiceManager.getService()
+ .registerForNotifications("android.hardware.usb@1.0::IUsb",
+ "", serviceNotification);
+ if (!ret) {
+ logAndPrint(Log.ERROR, null,
+ "Failed to register service start notification");
+ }
+ } catch (RemoteException e) {
+ logAndPrintException(null,
+ "Failed to register service start notification", e);
+ return;
+ }
+ connectToProxy(mPw);
+ }
+
+ @Override
+ public void enableContaminantPresenceDetection(String portName, boolean enable,
+ long transactionId) {
+ synchronized (mLock) {
+ if (mProxy == null) {
+ logAndPrint(Log.ERROR, mPw, "Proxy is null. Retry !");
+ return;
+ }
+
+ try {
+ // Oneway call into the hal. Use the castFrom method from HIDL.
+ android.hardware.usb.V1_2.IUsb proxy =
+ android.hardware.usb.V1_2.IUsb.castFrom(mProxy);
+ proxy.enableContaminantPresenceDetection(portName, enable);
+ } catch (RemoteException e) {
+ logAndPrintException(mPw, "Failed to set contaminant detection", e);
+ } catch (ClassCastException e) {
+ logAndPrintException(mPw, "Method only applicable to V1.2 or above implementation",
+ e);
+ }
+ }
+ }
+
+ @Override
+ public void queryPortStatus(long transactionId) {
+ synchronized (mLock) {
+ if (mProxy == null) {
+ logAndPrint(Log.ERROR, mPw, "Proxy is null. Retry !");
+ return;
+ }
+
+ try {
+ mProxy.queryPortStatus();
+ } catch (RemoteException e) {
+ logAndPrintException(null, "ServiceStart: Failed to query port status", e);
+ }
+ }
+ }
+
+ @Override
+ public void switchMode(String portId, @HalUsbPortMode int newMode, long transactionId) {
+ synchronized (mLock) {
+ if (mProxy == null) {
+ logAndPrint(Log.ERROR, mPw, "Proxy is null. Retry !");
+ return;
+ }
+
+ PortRole newRole = new PortRole();
+ newRole.type = PortRoleType.MODE;
+ newRole.role = newMode;
+ try {
+ mProxy.switchRole(portId, newRole);
+ } catch (RemoteException e) {
+ logAndPrintException(mPw, "Failed to set the USB port mode: "
+ + "portId=" + portId
+ + ", newMode=" + UsbPort.modeToString(newRole.role), e);
+ }
+ }
+ }
+
+ @Override
+ public void switchPowerRole(String portId, @HalUsbPowerRole int newPowerRole,
+ long transactionId) {
+ synchronized (mLock) {
+ if (mProxy == null) {
+ logAndPrint(Log.ERROR, mPw, "Proxy is null. Retry !");
+ return;
+ }
+
+ PortRole newRole = new PortRole();
+ newRole.type = PortRoleType.POWER_ROLE;
+ newRole.role = newPowerRole;
+ try {
+ mProxy.switchRole(portId, newRole);
+ } catch (RemoteException e) {
+ logAndPrintException(mPw, "Failed to set the USB power role: portId=" + portId
+ + ", newPowerRole=" + UsbPort.powerRoleToString(newRole.role), e);
+ }
+ }
+ }
+
+ @Override
+ public void enableLimitPowerTransfer(String portName, boolean limit, long transactionId,
+ IUsbOperationInternal callback) {
+ /* Not supported in HIDL hals*/
+ try {
+ callback.onOperationComplete(USB_OPERATION_ERROR_NOT_SUPPORTED);
+ } catch (RemoteException e) {
+ logAndPrintException(mPw, "Failed to call onOperationComplete", e);
+ }
+ }
+
+ @Override
+ public void enableUsbDataWhileDocked(String portName, long transactionId,
+ IUsbOperationInternal callback) {
+ /* Not supported in HIDL hals*/
+ try {
+ callback.onOperationComplete(USB_OPERATION_ERROR_NOT_SUPPORTED);
+ } catch (RemoteException e) {
+ logAndPrintException(mPw, "Failed to call onOperationComplete", e);
+ }
+ }
+
+ @Override
+ public void switchDataRole(String portId, @HalUsbDataRole int newDataRole, long transactionId) {
+ synchronized (mLock) {
+ if (mProxy == null) {
+ logAndPrint(Log.ERROR, mPw, "Proxy is null. Retry !");
+ return;
+ }
+
+ PortRole newRole = new PortRole();
+ newRole.type = PortRoleType.DATA_ROLE;
+ newRole.role = newDataRole;
+ try {
+ mProxy.switchRole(portId, newRole);
+ } catch (RemoteException e) {
+ logAndPrintException(mPw, "Failed to set the USB data role: portId=" + portId
+ + ", newDataRole=" + UsbPort.dataRoleToString(newRole.role), e);
+ }
+ }
+ }
+
+ @Override
+ public boolean enableUsbData(String portName, boolean enable, long transactionId,
+ IUsbOperationInternal callback) {
+ int halVersion;
+
+ try {
+ halVersion = getUsbHalVersion();
+ } catch (RemoteException e) {
+ logAndPrintException(mPw, "Failed to query USB HAL version. opID:"
+ + transactionId
+ + " portId:" + portName, e);
+ return false;
+ }
+
+ if (halVersion != USB_HAL_V1_3) {
+ try {
+ callback.onOperationComplete(USB_OPERATION_ERROR_NOT_SUPPORTED);
+ } catch (RemoteException e) {
+ logAndPrintException(mPw, "Failed to call onOperationComplete. opID:"
+ + transactionId
+ + " portId:" + portName, e);
+ }
+ return false;
+ }
+
+ boolean success;
+ synchronized(mLock) {
+ try {
+ android.hardware.usb.V1_3.IUsb proxy
+ = android.hardware.usb.V1_3.IUsb.castFrom(mProxy);
+ success = proxy.enableUsbDataSignal(enable);
+ } catch (RemoteException e) {
+ logAndPrintException(mPw, "Failed enableUsbData: opId:" + transactionId
+ + " portId=" + portName , e);
+ try {
+ callback.onOperationComplete(USB_OPERATION_ERROR_INTERNAL);
+ } catch (RemoteException r) {
+ logAndPrintException(mPw, "Failed to call onOperationComplete. opID:"
+ + transactionId
+ + " portId:" + portName, r);
+ }
+ return false;
+ }
+ }
+ if (success) {
+ sUsbDataStatus = enable ? USB_DATA_STATUS_UNKNOWN : USB_DATA_STATUS_DISABLED_FORCE;
+ }
+ try {
+ callback.onOperationComplete(success
+ ? USB_OPERATION_SUCCESS
+ : USB_OPERATION_ERROR_INTERNAL);
+ } catch (RemoteException r) {
+ logAndPrintException(mPw, "Failed to call onOperationComplete. opID:"
+ + transactionId
+ + " portId:" + portName, r);
+ }
+ return false;
+ }
+
+ private static class HALCallback extends IUsbCallback.Stub {
+ public IndentingPrintWriter mPw;
+ public UsbPortManager mPortManager;
+ public UsbPortHidl mUsbPortHidl;
+
+ HALCallback(IndentingPrintWriter pw, UsbPortManager portManager, UsbPortHidl usbPortHidl) {
+ this.mPw = pw;
+ this.mPortManager = portManager;
+ this.mUsbPortHidl = usbPortHidl;
+ }
+
+ public void notifyPortStatusChange(
+ ArrayList<android.hardware.usb.V1_0.PortStatus> currentPortStatus, int retval) {
+ if (!mUsbPortHidl.mSystemReady) {
+ return;
+ }
+
+ if (retval != Status.SUCCESS) {
+ UsbPortManager.logAndPrint(Log.ERROR, mPw, "port status enquiry failed");
+ return;
+ }
+
+ ArrayList<RawPortInfo> newPortInfo = new ArrayList<>();
+
+ for (android.hardware.usb.V1_0.PortStatus current : currentPortStatus) {
+ RawPortInfo temp = new RawPortInfo(current.portName,
+ current.supportedModes, CONTAMINANT_PROTECTION_NONE,
+ current.currentMode,
+ current.canChangeMode, current.currentPowerRole,
+ current.canChangePowerRole,
+ current.currentDataRole, current.canChangeDataRole,
+ false, CONTAMINANT_PROTECTION_NONE,
+ false, CONTAMINANT_DETECTION_NOT_SUPPORTED, new int[sUsbDataStatus],
+ false, POWER_BRICK_STATUS_UNKNOWN);
+ newPortInfo.add(temp);
+ UsbPortManager.logAndPrint(Log.INFO, mPw, "ClientCallback V1_0: "
+ + current.portName);
+ }
+
+ mPortManager.updatePorts(newPortInfo);
+ }
+
+
+ public void notifyPortStatusChange_1_1(ArrayList<PortStatus_1_1> currentPortStatus,
+ int retval) {
+ if (!mUsbPortHidl.mSystemReady) {
+ return;
+ }
+
+ if (retval != Status.SUCCESS) {
+ UsbPortManager.logAndPrint(Log.ERROR, mPw, "port status enquiry failed");
+ return;
+ }
+
+ ArrayList<RawPortInfo> newPortInfo = new ArrayList<>();
+
+ int numStatus = currentPortStatus.size();
+ for (int i = 0; i < numStatus; i++) {
+ PortStatus_1_1 current = currentPortStatus.get(i);
+ RawPortInfo temp = new RawPortInfo(current.status.portName,
+ current.supportedModes, CONTAMINANT_PROTECTION_NONE,
+ current.currentMode,
+ current.status.canChangeMode, current.status.currentPowerRole,
+ current.status.canChangePowerRole,
+ current.status.currentDataRole, current.status.canChangeDataRole,
+ false, CONTAMINANT_PROTECTION_NONE,
+ false, CONTAMINANT_DETECTION_NOT_SUPPORTED, new int[sUsbDataStatus],
+ false, POWER_BRICK_STATUS_UNKNOWN);
+ newPortInfo.add(temp);
+ UsbPortManager.logAndPrint(Log.INFO, mPw, "ClientCallback V1_1: "
+ + current.status.portName);
+ }
+ mPortManager.updatePorts(newPortInfo);
+ }
+
+ public void notifyPortStatusChange_1_2(
+ ArrayList<PortStatus> currentPortStatus, int retval) {
+ if (!mUsbPortHidl.mSystemReady) {
+ return;
+ }
+
+ if (retval != Status.SUCCESS) {
+ UsbPortManager.logAndPrint(Log.ERROR, mPw, "port status enquiry failed");
+ return;
+ }
+
+ ArrayList<RawPortInfo> newPortInfo = new ArrayList<>();
+
+ int numStatus = currentPortStatus.size();
+ for (int i = 0; i < numStatus; i++) {
+ PortStatus current = currentPortStatus.get(i);
+ RawPortInfo temp = new RawPortInfo(current.status_1_1.status.portName,
+ current.status_1_1.supportedModes,
+ current.supportedContaminantProtectionModes,
+ current.status_1_1.currentMode,
+ current.status_1_1.status.canChangeMode,
+ current.status_1_1.status.currentPowerRole,
+ current.status_1_1.status.canChangePowerRole,
+ current.status_1_1.status.currentDataRole,
+ current.status_1_1.status.canChangeDataRole,
+ current.supportsEnableContaminantPresenceProtection,
+ current.contaminantProtectionStatus,
+ current.supportsEnableContaminantPresenceDetection,
+ current.contaminantDetectionStatus,
+ new int[sUsbDataStatus],
+ false, POWER_BRICK_STATUS_UNKNOWN);
+ newPortInfo.add(temp);
+ UsbPortManager.logAndPrint(Log.INFO, mPw, "ClientCallback V1_2: "
+ + current.status_1_1.status.portName);
+ }
+ mPortManager.updatePorts(newPortInfo);
+ }
+
+ public void notifyRoleSwitchStatus(String portName, PortRole role, int retval) {
+ if (retval == Status.SUCCESS) {
+ UsbPortManager.logAndPrint(Log.INFO, mPw, portName + " role switch successful");
+ } else {
+ UsbPortManager.logAndPrint(Log.ERROR, mPw, portName + " role switch failed");
+ }
+ }
+ }
+}
diff --git a/services/wallpapereffectsgeneration/OWNERS b/services/wallpapereffectsgeneration/OWNERS
new file mode 100644
index 000000000000..d2d3e2c0a7b6
--- /dev/null
+++ b/services/wallpapereffectsgeneration/OWNERS
@@ -0,0 +1,4 @@
+susharon@google.com
+shanh@google.com
+huiwu@google.com
+srazdan@google.com
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index ce9530c196ef..02c137990202 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -43,6 +43,7 @@ import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
/**
@@ -571,7 +572,7 @@ public final class Call {
public static final int CAPABILITY_REMOTE_PARTY_SUPPORTS_RTT = 0x10000000;
//******************************************************************************************
- // Next CAPABILITY value: 0x20000000
+ // Next CAPABILITY value: 0x40000000
//******************************************************************************************
/**
@@ -733,6 +734,8 @@ public final class Call {
private final String mContactDisplayName;
private final @CallDirection int mCallDirection;
private final @Connection.VerificationStatus int mCallerNumberVerificationStatus;
+ private final CallEndpoint mActiveCallEndpoint;
+ private final Set<CallEndpoint> mAvailableCallEndpoint;
/**
* Whether the supplied capabilities supports the specified capability.
@@ -1116,32 +1119,52 @@ public final class Call {
return mCallerNumberVerificationStatus;
}
+ /**
+ * Return set of available {@link CallEndpoint} which can be used to push or answer this
+ * call via {@link #pushCall(CallEndpoint)} or {@link #answerCall(CallEndpoint, int)}.
+ * @return Set of available call endpoints.
+ */
+ public @NonNull Set<CallEndpoint> getAvailableCallEndpoints() {
+ return mAvailableCallEndpoint;
+ }
+
+ /**
+ * Return the {@link CallEndpoint} which is currently active for a call. If the call does
+ * not take place via any {@link CallEndpoint}, return {@code null}.
+ * @return Current active endpoint.
+ */
+ public @Nullable CallEndpoint getActiveCallEndpoint() {
+ return mActiveCallEndpoint;
+ }
+
@Override
public boolean equals(Object o) {
if (o instanceof Details) {
Details d = (Details) o;
return
- Objects.equals(mState, d.mState) &&
- Objects.equals(mHandle, d.mHandle) &&
- Objects.equals(mHandlePresentation, d.mHandlePresentation) &&
- Objects.equals(mCallerDisplayName, d.mCallerDisplayName) &&
- Objects.equals(mCallerDisplayNamePresentation,
- d.mCallerDisplayNamePresentation) &&
- Objects.equals(mAccountHandle, d.mAccountHandle) &&
- Objects.equals(mCallCapabilities, d.mCallCapabilities) &&
- Objects.equals(mCallProperties, d.mCallProperties) &&
- Objects.equals(mDisconnectCause, d.mDisconnectCause) &&
- Objects.equals(mConnectTimeMillis, d.mConnectTimeMillis) &&
- Objects.equals(mGatewayInfo, d.mGatewayInfo) &&
- Objects.equals(mVideoState, d.mVideoState) &&
- Objects.equals(mStatusHints, d.mStatusHints) &&
- areBundlesEqual(mExtras, d.mExtras) &&
- areBundlesEqual(mIntentExtras, d.mIntentExtras) &&
- Objects.equals(mCreationTimeMillis, d.mCreationTimeMillis) &&
- Objects.equals(mContactDisplayName, d.mContactDisplayName) &&
- Objects.equals(mCallDirection, d.mCallDirection) &&
- Objects.equals(mCallerNumberVerificationStatus,
- d.mCallerNumberVerificationStatus);
+ Objects.equals(mState, d.mState)
+ && Objects.equals(mHandle, d.mHandle)
+ && Objects.equals(mHandlePresentation, d.mHandlePresentation)
+ && Objects.equals(mCallerDisplayName, d.mCallerDisplayName)
+ && Objects.equals(mCallerDisplayNamePresentation,
+ d.mCallerDisplayNamePresentation)
+ && Objects.equals(mAccountHandle, d.mAccountHandle)
+ && Objects.equals(mCallCapabilities, d.mCallCapabilities)
+ && Objects.equals(mCallProperties, d.mCallProperties)
+ && Objects.equals(mDisconnectCause, d.mDisconnectCause)
+ && Objects.equals(mConnectTimeMillis, d.mConnectTimeMillis)
+ && Objects.equals(mGatewayInfo, d.mGatewayInfo)
+ && Objects.equals(mVideoState, d.mVideoState)
+ && Objects.equals(mStatusHints, d.mStatusHints)
+ && areBundlesEqual(mExtras, d.mExtras)
+ && areBundlesEqual(mIntentExtras, d.mIntentExtras)
+ && Objects.equals(mCreationTimeMillis, d.mCreationTimeMillis)
+ && Objects.equals(mContactDisplayName, d.mContactDisplayName)
+ && Objects.equals(mCallDirection, d.mCallDirection)
+ && Objects.equals(mCallerNumberVerificationStatus,
+ d.mCallerNumberVerificationStatus)
+ && Objects.equals(mActiveCallEndpoint, d.mActiveCallEndpoint)
+ && Objects.equals(mAvailableCallEndpoint, d.mAvailableCallEndpoint);
}
return false;
}
@@ -1190,7 +1213,9 @@ public final class Call {
long creationTimeMillis,
String contactDisplayName,
int callDirection,
- int callerNumberVerificationStatus) {
+ int callerNumberVerificationStatus,
+ CallEndpoint activeCallEndpoint,
+ Set<CallEndpoint> availableCallEndpoints) {
mState = state;
mTelecomCallId = telecomCallId;
mHandle = handle;
@@ -1211,6 +1236,8 @@ public final class Call {
mContactDisplayName = contactDisplayName;
mCallDirection = callDirection;
mCallerNumberVerificationStatus = callerNumberVerificationStatus;
+ mActiveCallEndpoint = activeCallEndpoint;
+ mAvailableCallEndpoint = availableCallEndpoints;
}
/** {@hide} */
@@ -1235,7 +1262,9 @@ public final class Call {
parcelableCall.getCreationTimeMillis(),
parcelableCall.getContactDisplayName(),
parcelableCall.getCallDirection(),
- parcelableCall.getCallerNumberVerificationStatus());
+ parcelableCall.getCallerNumberVerificationStatus(),
+ parcelableCall.getActiveCallEndpoint(),
+ parcelableCall.getAvailableCallEndpoints());
}
@Override
@@ -1257,6 +1286,10 @@ public final class Call {
sb.append(capabilitiesToString(mCallCapabilities));
sb.append(", props: ");
sb.append(propertiesToString(mCallProperties));
+ sb.append(", activeEndpoint: ");
+ sb.append(mActiveCallEndpoint);
+ sb.append(", availableEndpoints: ");
+ sb.append(mAvailableCallEndpoint);
sb.append("]");
return sb.toString();
}
@@ -1356,6 +1389,121 @@ public final class Call {
public static final int HANDOVER_FAILURE_UNKNOWN = 5;
/**
+ * @hide
+ */
+ @IntDef(prefix = { "PUSH_FAILED_" },
+ value = {PUSH_FAILED_UNKNOWN_REASON, PUSH_FAILED_ENDPOINT_UNAVAILABLE,
+ PUSH_FAILED_ENDPOINT_TIMEOUT, PUSH_FAILED_ENDPOINT_REJECTED})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface PushFailedReason {}
+
+ /**
+ * Answer failure reason returned via {@link #onAnswerFailed(CallEndpoint, int)} when a push
+ * fails due to unknown reason.
+ * <p>
+ * For more information on push call, see {@link #pushCall(CallEndpoint)}.
+ */
+ public static final int PUSH_FAILED_UNKNOWN_REASON = 0;
+
+ /**
+ * Push failure reason returned via {@link #onCallPushFailed(CallEndpoint, int)} when a push
+ * fails due to requested endpoint is unavailable.
+ * <p>
+ * For more information on push call, see {@link #pushCall(CallEndpoint)}.
+ */
+ public static final int PUSH_FAILED_ENDPOINT_UNAVAILABLE = 1;
+
+ /**
+ * Push failure reason returned via {@link #onCallPushFailed(CallEndpoint, int)} when a push
+ * fails due to requested endpoint takes too long to handle the request.
+ * <p>
+ * For more information on push call, see {@link #pushCall(CallEndpoint)}.
+ */
+ public static final int PUSH_FAILED_ENDPOINT_TIMEOUT = 2;
+
+ /**
+ * Push failure reason returned via {@link #onCallPushFailed(CallEndpoint, int)} when a push
+ * fails due to endpoint rejected the request.
+ * <p>
+ * For more information on push call, see {@link #pushCall(CallEndpoint)}.
+ */
+ public static final int PUSH_FAILED_ENDPOINT_REJECTED = 3;
+
+ /**
+ * @hide
+ */
+ @IntDef(prefix = { "ANSWER_FAILED_" },
+ value = {ANSWER_FAILED_UNKNOWN_REASON, ANSWER_FAILED_ENDPOINT_UNAVAILABLE,
+ ANSWER_FAILED_ENDPOINT_TIMEOUT, ANSWER_FAILED_ENDPOINT_REJECTED})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface AnswerFailedReason {}
+
+ /**
+ * Answer failure reason returned via {@link #onAnswerFailed(CallEndpoint, int)} when it
+ * fails due to unknown reason.
+ * <p>
+ * For more information on answer call, see {@link #answerCall(CallEndpoint, int)}.
+ */
+ public static final int ANSWER_FAILED_UNKNOWN_REASON = 0;
+
+ /**
+ * Answer failure reason returned via {@link #onAnswerFailed(CallEndpoint, int)} when it
+ * fails due to requested endpoint is unavailable.
+ * <p>
+ * For more information on answer call, see {@link #answerCall(CallEndpoint, int)}.
+ */
+ public static final int ANSWER_FAILED_ENDPOINT_UNAVAILABLE = 1;
+
+ /**
+ * Answer failure reason returned via {@link #onAnswerFailed(CallEndpoint, int)} when it
+ * fails due to requested endpoint takes too long to handle the request.
+ * <p>
+ * For more information on answer call, see {@link #answerCall(CallEndpoint, int)}.
+ */
+ public static final int ANSWER_FAILED_ENDPOINT_TIMEOUT = 2;
+
+ /**
+ * Answer failure reason returned via {@link #onAnswerFailed(CallEndpoint, int)} when it
+ * fails due to endpoint rejected the request.
+ * <p>
+ * For more information on answer call, see {@link #answerCall(CallEndpoint, int)}.
+ */
+ public static final int ANSWER_FAILED_ENDPOINT_REJECTED = 3;
+
+ /**
+ * @hide
+ */
+ @IntDef(prefix = { "PULL_FAILED_" },
+ value = {PULL_FAILED_UNKNOWN_REASON, PULL_FAILED_ENDPOINT_TIMEOUT,
+ PULL_FAILED_ENDPOINT_REJECTED})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface PullFailedReason {}
+
+ /**
+ * Pull failure reason returned via {@link #onCallPullFailed(int)} when it fails due to
+ * unknown reason.
+ * <p>
+ * For more information on pull call, see {@link #pullCall()}.
+ */
+ public static final int PULL_FAILED_UNKNOWN_REASON = 0;
+
+ /**
+ * Pull failure reason returned via {@link #onCallPullFailed(int)} when it fails due to
+ * requested endpoint takes too long to handle the request.
+ * <p>
+ * For more information on pull call, see {@link #pullCall()}.
+ */
+ public static final int PULL_FAILED_ENDPOINT_TIMEOUT = 1;
+
+ /**
+ * Pull failure reason returned via {@link #onCallPullFailed(int)} when it fails due to
+ * endpoint rejected the request.
+ * <p>
+ * For more information on pull call, see {@link #pullCall()}.
+ */
+ public static final int PULL_FAILED_ENDPOINT_REJECTED = 2;
+
+ /**
* Invoked when the state of this {@code Call} has changed. See {@link #getState()}.
*
* @param call The {@code Call} invoking this method.
@@ -1515,6 +1663,31 @@ public final class Call {
* @param failureReason Error reason for failure.
*/
public void onHandoverFailed(Call call, @HandoverFailureErrors int failureReason) {}
+
+ /**
+ * Invoked when call push request via {@link #pushCall(CallEndpoint)} has failed.
+ *
+ * @param endpoint The endpoint requested to push the call to.
+ * @param reason Failed reason.
+ */
+ public void onCallPushFailed(@NonNull CallEndpoint endpoint, @PushFailedReason int reason)
+ {}
+
+ /**
+ * Invoked when answer call request via {@link #answerCall(CallEndpoint, int)} has failed.
+ *
+ * @param endpoint The endpoint requested to answer the call.
+ * @param reason Failed reason
+ */
+ public void onAnswerFailed(@NonNull CallEndpoint endpoint, @AnswerFailedReason int reason)
+ {}
+
+ /**
+ * Invoked when pull call request via {@link #pullCall()} has failed.
+ *
+ * @param reason Failed reason
+ */
+ public void onCallPullFailed(@PullFailedReason int reason) {}
}
/**
@@ -1936,8 +2109,21 @@ public final class Call {
}
/**
+ * @deprecated Use {@link #pullCall()} instead
+ */
+ @Deprecated
+ public void pullExternalCall() {
+ // If this isn't an external call, ignore the request.
+ if (!mDetails.hasProperty(Details.PROPERTY_IS_EXTERNAL_CALL)) {
+ return;
+ }
+
+ mInCallAdapter.pullExternalCall(mTelecomCallId);
+ }
+
+ /**
* Initiates a request to the {@link ConnectionService} to pull an external call to the local
- * device.
+ * device, or to bring a tethered call back to the local device.
* <p>
* Calls to this method are ignored if the call does not have the
* {@link Call.Details#PROPERTY_IS_EXTERNAL_CALL} property set.
@@ -1946,13 +2132,34 @@ public final class Call {
* {@link TelecomManager#METADATA_INCLUDE_EXTERNAL_CALLS} metadata set to {@code true}
* in its manifest.
*/
- public void pullExternalCall() {
- // If this isn't an external call, ignore the request.
- if (!mDetails.hasProperty(Details.PROPERTY_IS_EXTERNAL_CALL)) {
- return;
- }
+ public void pullCall() {
+ pullExternalCall();
+ }
- mInCallAdapter.pullExternalCall(mTelecomCallId);
+ /**
+ * Initiates a request to the {@link ConnectionService} to push a call to a
+ * {@link CallEndpoint}.
+ * <p>
+ *
+ * @param endpoint The call endpoint to which the call will be pushed.
+ */
+ public void pushCall(@NonNull CallEndpoint endpoint) {
+ mInCallAdapter.pushCall(mTelecomCallId, endpoint);
+ }
+
+ /**
+ * Initiates a request to the {@link ConnectionService} to answer a call to a
+ * {@link CallEndpoint}.
+ * <p>
+ * Calls to this method are ignored if the call does not have the
+ * {@link Call.Details#CAPABILITY_CAN_PULL_CALL} capability set.
+ *
+ * @param endpoint The call endpoint on which to answer the call.
+ * @param videoState The video state in which to answer the call.
+ */
+ public void answerCall(@NonNull CallEndpoint endpoint,
+ @VideoProfile.VideoState int videoState) {
+ mInCallAdapter.answerCall(mTelecomCallId, endpoint, videoState);
}
/**
@@ -2633,7 +2840,9 @@ public final class Call {
mDetails.getCreationTimeMillis(),
mDetails.getContactDisplayName(),
mDetails.getCallDirection(),
- mDetails.getCallerNumberVerificationStatus()
+ mDetails.getCallerNumberVerificationStatus(),
+ mDetails.getActiveCallEndpoint(),
+ mDetails.getAvailableCallEndpoints()
);
fireDetailsChanged(mDetails);
}
@@ -2675,7 +2884,7 @@ public final class Call {
}
/** {@hide} */
- final void internalOnHandoverComplete() {
+ void internalOnHandoverComplete() {
for (CallbackRecord<Callback> record : mCallbackRecords) {
final Call call = this;
final Callback callback = record.getCallback();
@@ -2683,6 +2892,32 @@ public final class Call {
}
}
+ /** {@hide} */
+ void internalOnCallPullFailed(@Callback.PullFailedReason int reason) {
+ for (CallbackRecord<Callback> record : mCallbackRecords) {
+ final Callback callback = record.getCallback();
+ record.getHandler().post(() -> callback.onCallPullFailed(reason));
+ }
+ }
+
+ /** {@hide} */
+ void internalOnCallPushFailed(CallEndpoint callEndpoint,
+ @Callback.PushFailedReason int reason) {
+ for (CallbackRecord<Callback> record : mCallbackRecords) {
+ final Callback callback = record.getCallback();
+ record.getHandler().post(() -> callback.onCallPushFailed(callEndpoint, reason));
+ }
+ }
+
+ /** {@hide} */
+ void internalOnAnswerFailed(CallEndpoint callEndpoint,
+ @Callback.AnswerFailedReason int reason) {
+ for (CallbackRecord<Callback> record : mCallbackRecords) {
+ final Callback callback = record.getCallback();
+ record.getHandler().post(() -> callback.onAnswerFailed(callEndpoint, reason));
+ }
+ }
+
private void fireStateChanged(final int newState) {
for (CallbackRecord<Callback> record : mCallbackRecords) {
final Call call = this;
diff --git a/telecomm/java/android/telecom/CallAudioState.java b/telecomm/java/android/telecom/CallAudioState.java
index 55957bd85eaa..389df80497df 100644
--- a/telecomm/java/android/telecom/CallAudioState.java
+++ b/telecomm/java/android/telecom/CallAudioState.java
@@ -259,10 +259,10 @@ public final class CallAudioState implements Parcelable {
int route = source.readInt();
int supportedRouteMask = source.readInt();
BluetoothDevice activeBluetoothDevice = source.readParcelable(
- ClassLoader.getSystemClassLoader());
+ ClassLoader.getSystemClassLoader(), android.bluetooth.BluetoothDevice.class);
List<BluetoothDevice> supportedBluetoothDevices = new ArrayList<>();
source.readParcelableList(supportedBluetoothDevices,
- ClassLoader.getSystemClassLoader());
+ ClassLoader.getSystemClassLoader(), android.bluetooth.BluetoothDevice.class);
return new CallAudioState(isMuted, route,
supportedRouteMask, activeBluetoothDevice, supportedBluetoothDevices);
}
diff --git a/telecomm/java/android/telecom/CallEndpoint.aidl b/telecomm/java/android/telecom/CallEndpoint.aidl
new file mode 100644
index 000000000000..5030ffd62d7a
--- /dev/null
+++ b/telecomm/java/android/telecom/CallEndpoint.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telecom;
+
+/**
+ * {@hide}
+ */
+parcelable CallEndpoint;
diff --git a/telecomm/java/android/telecom/CallEndpoint.java b/telecomm/java/android/telecom/CallEndpoint.java
new file mode 100644
index 000000000000..dc70656983bc
--- /dev/null
+++ b/telecomm/java/android/telecom/CallEndpoint.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.telecom;
+
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.content.ComponentName;
+import android.os.Parcel;
+import android.os.ParcelUuid;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Represents the endpoint on which a call can be carried by the user.
+ *
+ * For example, the user may be able to carry out a call on another device on their local network
+ * using a call streaming solution, or may be able to carry out a call on another device registered
+ * with the same mobile line of service.
+ */
+public final class CallEndpoint implements Parcelable {
+ /**
+ * @hide
+ */
+ @IntDef(prefix = {"ENDPOINT_TYPE_"},
+ value = {ENDPOINT_TYPE_TETHERED, ENDPOINT_TYPE_UNTETHERED})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface EndpointType {}
+
+ /** Indicates the endpoint contains a complete calling stack and is capable of carrying out a
+ * call on its own. Untethered endpoints are typically other devices which share the same
+ * mobile line of service as the current device.
+ */
+ public static final int ENDPOINT_TYPE_UNTETHERED = 1;
+
+ /** Indicates the endpoint itself doesn't have the required calling infrastructure in order to
+ * complete a call on its own. Tethered endpoints depend on a call streaming solution to
+ * transport the media and control for a call to another device, while depending on the current
+ * device to connect the call to the mobile network.
+ */
+ public static final int ENDPOINT_TYPE_TETHERED = 2;
+
+ private final ParcelUuid mUuid;
+ private CharSequence mDescription;
+ private final int mType;
+ private final ComponentName mComponentName;
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ mUuid.writeToParcel(dest, flags);
+ dest.writeCharSequence(mDescription);
+ dest.writeInt(mType);
+ mComponentName.writeToParcel(dest, flags);
+ }
+
+ public static final @android.annotation.NonNull Creator<CallEndpoint> CREATOR =
+ new Creator<CallEndpoint>() {
+ @Override
+ public CallEndpoint createFromParcel(Parcel in) {
+ return new CallEndpoint(in);
+ }
+
+ @Override
+ public CallEndpoint[] newArray(int size) {
+ return new CallEndpoint[size];
+ }
+ };
+
+ public CallEndpoint(@NonNull ParcelUuid uuid, @NonNull CharSequence description, int type,
+ @NonNull ComponentName componentName) {
+ mUuid = uuid;
+ mDescription = description;
+ mType = type;
+ mComponentName = componentName;
+ }
+
+ private CallEndpoint(@NonNull Parcel in) {
+ this(ParcelUuid.CREATOR.createFromParcel(in), in.readCharSequence(), in.readInt(),
+ ComponentName.CREATOR.createFromParcel(in));
+ }
+
+ /**
+ * A unique identifier for this call endpoint. An endpoint provider should take care to use an
+ * identifier which is stable for the current association between an endpoint and the current
+ * device, but which is not globally identifying.
+ * @return the unique identifier.
+ */
+ public @NonNull ParcelUuid getIdentifier() {
+ return mUuid;
+ }
+
+ /**
+ * A human-readable description of this {@link CallEndpoint}. An {@link InCallService} uses
+ * when informing the user of the endpoint.
+ * @return the description.
+ */
+ public @NonNull CharSequence getDescription() {
+ return mDescription;
+ }
+
+ public @EndpointType int getType() {
+ return mType;
+ }
+
+ /**
+ * @hide
+ */
+ public @NonNull ComponentName getComponentName() {
+ return mComponentName;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof CallEndpoint) {
+ CallEndpoint d = (CallEndpoint) o;
+ return Objects.equals(mUuid, d.mUuid)
+ && Objects.equals(mDescription, d.mDescription)
+ && Objects.equals(mType, d.mType)
+ && Objects.equals(mComponentName, d.mComponentName);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mUuid, mDescription, mType, mComponentName);
+ }
+}
diff --git a/telecomm/java/android/telecom/CallEndpointCallback.java b/telecomm/java/android/telecom/CallEndpointCallback.java
new file mode 100644
index 000000000000..6ba55f103d5c
--- /dev/null
+++ b/telecomm/java/android/telecom/CallEndpointCallback.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.telecom;
+
+/**
+ * Provides callbacks from telecom to the cross device call streaming app with lifecycle events
+ * related to an {@link CallEndpointSession}.
+ */
+public interface CallEndpointCallback {
+ /**
+ * Invoked by telecom when a {@link CallEndpointSession} is started but the streaming app has
+ * not activated the endpoint in a timely manner and the framework deems the activation request
+ * to have timed out.
+ */
+ void onCallEndpointSessionActivationTimeout();
+
+ /**
+ * Invoked by telecom when {@link CallEndpointSession#setCallEndpointSessionDeactivated()}
+ * called by a cross device call streaming app, or when the app uninstalled. When a tethered
+ * {@link CallEndpoint} is deactivated, the call streaming app should clean up any
+ * audio/network resources and stop relaying call controls from the endpoint.
+ */
+ void onCallEndpointSessionDeactivated();
+}
diff --git a/telecomm/java/android/telecom/CallEndpointSession.java b/telecomm/java/android/telecom/CallEndpointSession.java
new file mode 100644
index 000000000000..1e7b30c79ee0
--- /dev/null
+++ b/telecomm/java/android/telecom/CallEndpointSession.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.telecom;
+
+import android.annotation.IntDef;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.RemoteException;
+
+import com.android.internal.telecom.ICallEndpointSession;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+
+/**
+ * Provides method and necessary information for cross device call streaming app to streams calls
+ * and updates to the status of the endpoint.
+ *
+ */
+public class CallEndpointSession {
+ /**
+ * Indicates that this call endpoint session is activated by
+ * {@link Call#answerCall(CallEndpoint, int)} from the original device.
+ */
+ public static final int ANSWER_REQUEST = 1;
+
+ /**
+ * Indicates that this call endpoint session is activated by {@link Call#pushCall(CallEndpoint)}
+ * from the original device.
+ */
+ public static final int PUSH_REQUEST = 2;
+
+ /**
+ * Indicates that this call endpoint session is activated by
+ * {@link TelecomManager#placeCall(Uri, Bundle)} with extra
+ * {@link TelecomManager#EXTRA_START_CALL_ON_ENDPOINT} set.
+ */
+ public static final int PLACE_REQUEST = 3;
+
+ /**
+ * @hide
+ */
+ @IntDef(prefix = {"ACTIVATION_FAILURE_"},
+ value = {ACTIVATION_FAILURE_REJECTED, ACTIVATION_FAILURE_UNAVAILABLE})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ActivationFailureReason {}
+ /**
+ * Used as reason for {@link #setCallEndpointSessionActivationFailed(int)} to inform the
+ * endpoint is no longer present on the network.
+ */
+ public static final int ACTIVATION_FAILURE_UNAVAILABLE = 0;
+
+ /**
+ * Used as reason for {@link #setCallEndpointSessionActivationFailed(int)} to inform the
+ * remote endpoint rejected the request to start streaming a cross device call.
+ */
+ public static final int ACTIVATION_FAILURE_REJECTED = 1;
+
+ private final ICallEndpointSession mCallEndpointSession;
+
+ /**
+ * {@hide}
+ */
+ public CallEndpointSession(ICallEndpointSession callEndpointSession) {
+ mCallEndpointSession = callEndpointSession;
+ }
+
+ /**
+ * Invoked by cross device call streaming app to inform telecom stack that the call endpoint is
+ * now activated and that the call is being streamed to the endpoint.
+ */
+ public void setCallEndpointSessionActivated() {
+ try {
+ mCallEndpointSession.setCallEndpointSessionActivated();
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Invoked by cross device call streaming app to inform telecom stack that the call endpoint
+ * could not be activated due to error.
+ * Possible errors are:
+ * <ul>
+ * <li>{@link #ACTIVATION_FAILURE_UNAVAILABLE}</li>
+ * <li>{@link #ACTIVATION_FAILURE_REJECTED}</li>
+ * </ul>
+ *
+ * @param reason The reason for activation failure
+ */
+ public void setCallEndpointSessionActivationFailed(@ActivationFailureReason int reason) {
+ try {
+ mCallEndpointSession.setCallEndpointSessionActivationFailed(reason);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Invoked by cross device call streaming app to inform telecom stack that the call endpoint is
+ * no longer active.
+ */
+ public void setCallEndpointSessionDeactivated() {
+ try {
+ mCallEndpointSession.setCallEndpointSessionDeactivated();
+ } catch (RemoteException e) {
+ }
+ }
+}
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index d63cdc004a3d..21a180459978 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -561,15 +561,6 @@ public abstract class Connection extends Conferenceable {
*/
public static final int PROPERTY_CROSS_SIM = 1 << 13;
- /**
- * Connection is a tethered external call.
- * <p>
- * Indicates that the {@link Connection} is fixed on this device but the audio streams are
- * re-routed to another device.
- * <p>
- */
- public static final int PROPERTY_TETHERED_CALL = 1 << 14;
-
//**********************************************************************************************
// Next PROPERTY value: 1<<14
//**********************************************************************************************
@@ -3546,9 +3537,9 @@ public abstract class Connection extends Conferenceable {
mIsBlocked = in.readByte() != 0;
mIsInContacts = in.readByte() != 0;
CallScreeningService.ParcelableCallResponse response
- = in.readParcelable(CallScreeningService.class.getClassLoader());
+ = in.readParcelable(CallScreeningService.class.getClassLoader(), android.telecom.CallScreeningService.ParcelableCallResponse.class);
mCallResponse = response == null ? null : response.toCallResponse();
- mCallScreeningComponent = in.readParcelable(ComponentName.class.getClassLoader());
+ mCallScreeningComponent = in.readParcelable(ComponentName.class.getClassLoader(), android.content.ComponentName.class);
}
@NonNull
diff --git a/telecomm/java/android/telecom/ConnectionRequest.java b/telecomm/java/android/telecom/ConnectionRequest.java
index be5fae488d5e..1172e1392ef8 100644
--- a/telecomm/java/android/telecom/ConnectionRequest.java
+++ b/telecomm/java/android/telecom/ConnectionRequest.java
@@ -272,17 +272,17 @@ public final class ConnectionRequest implements Parcelable {
}
private ConnectionRequest(Parcel in) {
- mAccountHandle = in.readParcelable(getClass().getClassLoader());
- mAddress = in.readParcelable(getClass().getClassLoader());
- mExtras = in.readParcelable(getClass().getClassLoader());
+ mAccountHandle = in.readParcelable(getClass().getClassLoader(), android.telecom.PhoneAccountHandle.class);
+ mAddress = in.readParcelable(getClass().getClassLoader(), android.net.Uri.class);
+ mExtras = in.readParcelable(getClass().getClassLoader(), android.os.Bundle.class);
mVideoState = in.readInt();
mTelecomCallId = in.readString();
mShouldShowIncomingCallUi = in.readInt() == 1;
- mRttPipeFromInCall = in.readParcelable(getClass().getClassLoader());
- mRttPipeToInCall = in.readParcelable(getClass().getClassLoader());
+ mRttPipeFromInCall = in.readParcelable(getClass().getClassLoader(), android.os.ParcelFileDescriptor.class);
+ mRttPipeToInCall = in.readParcelable(getClass().getClassLoader(), android.os.ParcelFileDescriptor.class);
mParticipants = new ArrayList<Uri>();
- in.readList(mParticipants, getClass().getClassLoader());
+ in.readList(mParticipants, getClass().getClassLoader(), android.net.Uri.class);
mIsAdhocConference = in.readInt() == 1;
}
diff --git a/telecomm/java/android/telecom/DisconnectCause.java b/telecomm/java/android/telecom/DisconnectCause.java
index ed7b79f62753..63b954850a9e 100644
--- a/telecomm/java/android/telecom/DisconnectCause.java
+++ b/telecomm/java/android/telecom/DisconnectCause.java
@@ -111,6 +111,22 @@ public final class DisconnectCause implements Parcelable {
*/
public static final String REASON_EMERGENCY_CALL_PLACED = "REASON_EMERGENCY_CALL_PLACED";
+ /**
+ * This reason is set when an call is ended due to {@link CallEndpoint} rejection.
+ * This reason string should only be associated with the {@link #LOCAL} disconnect code returned
+ * from {@link #getCode()}.
+ */
+ public static final String REASON_ENDPOINT_REJECTED = "REASON_ENDPOINT_REJECTED";
+
+ /**
+ * This reason is set when a call is ended due to {@link CallEndpoint} deactivated by
+ * call disconnection or user terminated streaming.
+ * This reason string should only be associated with the {@link #LOCAL} disconnect code returned
+ * from {@link #getCode()}
+ */
+ public static final String REASON_ENDPOINT_SESSION_DEACTIVATED =
+ "REASON_ENDPOINT_SESSION_DEACTIVATED";
+
private int mDisconnectCode;
private CharSequence mDisconnectLabel;
private CharSequence mDisconnectDescription;
@@ -287,7 +303,7 @@ public final class DisconnectCause implements Parcelable {
int tone = source.readInt();
int telephonyDisconnectCause = source.readInt();
int telephonyPreciseDisconnectCause = source.readInt();
- ImsReasonInfo imsReasonInfo = source.readParcelable(null);
+ ImsReasonInfo imsReasonInfo = source.readParcelable(null, android.telephony.ims.ImsReasonInfo.class);
return new DisconnectCause(code, label, description, reason, tone,
telephonyDisconnectCause, telephonyPreciseDisconnectCause, imsReasonInfo);
}
diff --git a/telecomm/java/android/telecom/InCallAdapter.java b/telecomm/java/android/telecom/InCallAdapter.java
index ab35affe9099..34e9942a53c9 100755
--- a/telecomm/java/android/telecom/InCallAdapter.java
+++ b/telecomm/java/android/telecom/InCallAdapter.java
@@ -373,6 +373,34 @@ public final class InCallAdapter {
}
/**
+ * Instructs Telecom to push a call to the given endpoint.
+ *
+ * @param callId The callId to push.
+ * @param callEndpoint The endpoint to which the call will be pushed.
+ */
+ public void pushCall(String callId, CallEndpoint callEndpoint) {
+ try {
+ mAdapter.pushCall(callId, callEndpoint);
+ } catch (RemoteException ignored) {
+ }
+ }
+
+ /**
+ * Instructs Telecom to answer a call via the given endpoint.
+ *
+ * @param callId The callId to push.
+ * @param callEndpoint The endpoint on which the call will be answered.
+ * @param videoState The video state in which to answer the call.
+ */
+ public void answerCall(String callId, CallEndpoint callEndpoint,
+ @VideoProfile.VideoState int videoState) {
+ try {
+ mAdapter.answerCallViaEndpoint(callId, callEndpoint, videoState);
+ } catch (RemoteException ignored) {
+ }
+ }
+
+ /**
* Intructs Telecom to send a call event.
*
* @param callId The callId to send the event for.
diff --git a/telecomm/java/android/telecom/InCallService.java b/telecomm/java/android/telecom/InCallService.java
index 0ddd52dfc76d..ecd6596b0e7d 100644
--- a/telecomm/java/android/telecom/InCallService.java
+++ b/telecomm/java/android/telecom/InCallService.java
@@ -30,9 +30,12 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
+import android.os.RemoteException;
import android.view.Surface;
import com.android.internal.os.SomeArgs;
+import com.android.internal.telecom.ICallEndpointCallback;
+import com.android.internal.telecom.ICallEndpointSession;
import com.android.internal.telecom.IInCallAdapter;
import com.android.internal.telecom.IInCallService;
@@ -258,6 +261,10 @@ public abstract class InCallService extends Service {
private static final int MSG_ON_RTT_INITIATION_FAILURE = 11;
private static final int MSG_ON_HANDOVER_FAILED = 12;
private static final int MSG_ON_HANDOVER_COMPLETE = 13;
+ private static final int MSG_ON_PUSH_FAILED = 14;
+ private static final int MSG_ON_PULL_FAILED = 15;
+ private static final int MSG_ON_ANSWER_EXTERNAL_FAILED = 16;
+ private static final int MSG_ON_CALL_ENDPOINT_ACTIVATION_REQUEST = 17;
/** Default Handler used to consolidate binder method calls onto a single thread. */
private final Handler mHandler = new Handler(Looper.getMainLooper()) {
@@ -339,6 +346,66 @@ public abstract class InCallService extends Service {
mPhone.internalOnHandoverComplete(callId);
break;
}
+ case MSG_ON_PUSH_FAILED: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ String callId = (String) args.arg1;
+ CallEndpoint callEndpoint = (CallEndpoint) args.arg2;
+ int reason = (int) args.arg3;
+ mPhone.internalOnCallPushFailed(callId, callEndpoint, reason);
+ } finally {
+ args.recycle();
+ }
+ break;
+ }
+ case MSG_ON_PULL_FAILED: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ String callId = (String) args.arg1;
+ int reason = (int) args.arg2;
+ mPhone.internalOnCallPullFailed(callId, reason);
+ } finally {
+ args.recycle();
+ }
+ break;
+ }
+ case MSG_ON_ANSWER_EXTERNAL_FAILED: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ String callId = (String) args.arg1;
+ CallEndpoint callEndpoint = (CallEndpoint) args.arg2;
+ int reason = (int) args.arg3;
+ mPhone.internalOnAnswerFailed(callId, callEndpoint, reason);
+ } finally {
+ args.recycle();
+ }
+ break;
+ }
+ case MSG_ON_CALL_ENDPOINT_ACTIVATION_REQUEST: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ CallEndpoint callEndpoint = (CallEndpoint) args.arg1;
+ ICallEndpointSession iCallEndpointSession =
+ (ICallEndpointSession) args.arg2;
+ try {
+ mCallEndpointCallback = onCallEndpointActivationRequested(callEndpoint,
+ new CallEndpointSession(iCallEndpointSession));
+ } catch (UnsupportedOperationException e) {
+ // This InCallService neglected to implement
+ // onCallEndpointActivationRequested, immediately signal back to Telecom
+ // that the activation failed.
+ try {
+ iCallEndpointSession.setCallEndpointSessionActivationFailed(
+ CallEndpointSession.ACTIVATION_FAILURE_UNAVAILABLE);
+ } catch (RemoteException re) {
+ // Ignore
+ }
+ }
+ } finally {
+ args.recycle();
+ }
+ break;
+ }
default:
break;
}
@@ -353,6 +420,36 @@ public abstract class InCallService extends Service {
}
@Override
+ public ICallEndpointCallback requestCallEndpointActivation(CallEndpoint callEndpoint,
+ ICallEndpointSession callEndpointSession) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = callEndpoint;
+ args.arg2 = callEndpointSession;
+ mHandler.obtainMessage(MSG_ON_CALL_ENDPOINT_ACTIVATION_REQUEST, args).sendToTarget();
+
+ return new ICallEndpointCallback.Stub() {
+ @Override
+ public void onCallEndpointSessionActivationTimeout() throws RemoteException {
+ if (mCallEndpointCallback != null) {
+ mCallEndpointCallback.onCallEndpointSessionActivationTimeout();
+ }
+ }
+
+ @Override
+ public void onCallEndpointSessionDeactivated() throws RemoteException {
+ if (mCallEndpointCallback != null) {
+ mCallEndpointCallback.onCallEndpointSessionDeactivated();
+ }
+ }
+
+ @Override
+ public IBinder asBinder() {
+ return this;
+ }
+ };
+ }
+
+ @Override
public void addCall(ParcelableCall call) {
mHandler.obtainMessage(MSG_ADD_CALL, call).sendToTarget();
}
@@ -424,6 +521,32 @@ public abstract class InCallService extends Service {
public void onHandoverComplete(String callId) {
mHandler.obtainMessage(MSG_ON_HANDOVER_COMPLETE, callId).sendToTarget();
}
+
+ @Override
+ public void onCallPullFailed(String callId, int reason) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = callId;
+ args.arg2 = reason;
+ mHandler.obtainMessage(MSG_ON_PULL_FAILED, args).sendToTarget();
+ }
+
+ @Override
+ public void onCallPushFailed(String callId, CallEndpoint endpoint, int reason) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = callId;
+ args.arg2 = endpoint;
+ args.arg3 = reason;
+ mHandler.obtainMessage(MSG_ON_PUSH_FAILED, args).sendToTarget();
+ }
+
+ @Override
+ public void onAnswerFailed(String callId, CallEndpoint endpoint, int reason) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = callId;
+ args.arg2 = endpoint;
+ args.arg3 = reason;
+ mHandler.obtainMessage(MSG_ON_ANSWER_EXTERNAL_FAILED, args).sendToTarget();
+ }
}
private Phone.Listener mPhoneListener = new Phone.Listener() {
@@ -470,6 +593,8 @@ public abstract class InCallService extends Service {
};
private Phone mPhone;
+ private CallEndpointSession mCallEndpointSession;
+ private CallEndpointCallback mCallEndpointCallback;
public InCallService() {
}
@@ -494,6 +619,14 @@ public abstract class InCallService extends Service {
onPhoneDestroyed(oldPhone);
}
+ if (mCallEndpointCallback != null) {
+ mCallEndpointCallback = null;
+ }
+
+ if (mCallEndpointSession != null) {
+ mCallEndpointSession = null;
+ }
+
return false;
}
@@ -704,6 +837,21 @@ public abstract class InCallService extends Service {
}
/**
+ * To handle the request from telecom to activate an endpoint session. Streaming app with
+ * meta-data {@link TelecomManager#METADATA_STREAMING_TETHERED_CALLS}.
+ * @param endpoint The endpoint which is to be activated.
+ * @param session An instance of {@link CallEndpointSession} to let streaming app report updates
+ * of the endpoint.
+ * @return CallEndpointCallback The implementation provided by streaming app. Telecom use this
+ * to report events related to the call endpoint session.
+ */
+ public @NonNull CallEndpointCallback onCallEndpointActivationRequested(
+ @NonNull CallEndpoint endpoint, @NonNull CallEndpointSession session)
+ throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
* Used to issue commands to the {@link Connection.VideoProvider} associated with a
* {@link Call}.
*/
diff --git a/telecomm/java/android/telecom/ParcelableCall.java b/telecomm/java/android/telecom/ParcelableCall.java
index 320308c9e926..c42918324e23 100644
--- a/telecomm/java/android/telecom/ParcelableCall.java
+++ b/telecomm/java/android/telecom/ParcelableCall.java
@@ -16,6 +16,7 @@
package android.telecom;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.net.Uri;
@@ -29,8 +30,11 @@ import android.telecom.Call.Details.CallDirection;
import com.android.internal.telecom.IVideoProvider;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
/**
* Information about a call that is used between InCallService and Telecom.
@@ -69,6 +73,8 @@ public final class ParcelableCall implements Parcelable {
private int mCallerNumberVerificationStatus;
private String mContactDisplayName;
private String mActiveChildCallId;
+ private CallEndpoint mActiveCallEndpoint;
+ private Set<CallEndpoint> mAvailableCallEndpoints = new HashSet<>();
public ParcelableCallBuilder setId(String id) {
mId = id;
@@ -224,6 +230,27 @@ public final class ParcelableCall implements Parcelable {
return this;
}
+ /**
+ * Set active call endpoint
+ * @param callEndpoint
+ * @return
+ */
+ public ParcelableCallBuilder setActiveCallEndpoint(CallEndpoint callEndpoint) {
+ mActiveCallEndpoint = callEndpoint;
+ return this;
+ }
+
+ /**
+ * Set available call endpoints
+ * @param availableCallEndpoints
+ * @return
+ */
+ public ParcelableCallBuilder setAvailableCallEndpoints(
+ Set<CallEndpoint> availableCallEndpoints) {
+ mAvailableCallEndpoints = availableCallEndpoints;
+ return this;
+ }
+
public ParcelableCall createParcelableCall() {
return new ParcelableCall(
mId,
@@ -255,7 +282,9 @@ public final class ParcelableCall implements Parcelable {
mCallDirection,
mCallerNumberVerificationStatus,
mContactDisplayName,
- mActiveChildCallId);
+ mActiveChildCallId,
+ mActiveCallEndpoint,
+ mAvailableCallEndpoints);
}
public static ParcelableCallBuilder fromParcelableCall(ParcelableCall parcelableCall) {
@@ -292,6 +321,8 @@ public final class ParcelableCall implements Parcelable {
parcelableCall.mCallerNumberVerificationStatus;
newBuilder.mContactDisplayName = parcelableCall.mContactDisplayName;
newBuilder.mActiveChildCallId = parcelableCall.mActiveChildCallId;
+ newBuilder.mActiveCallEndpoint = parcelableCall.mActiveCallEndpoint;
+ newBuilder.mAvailableCallEndpoints = parcelableCall.mAvailableCallEndpoints;
return newBuilder;
}
}
@@ -327,6 +358,8 @@ public final class ParcelableCall implements Parcelable {
private final int mCallerNumberVerificationStatus;
private final String mContactDisplayName;
private final String mActiveChildCallId; // Only valid for CDMA conferences
+ private final CallEndpoint mActiveCallEndpoint;
+ private final Set<CallEndpoint> mAvailableCallEndpoints;
public ParcelableCall(
String id,
@@ -358,7 +391,9 @@ public final class ParcelableCall implements Parcelable {
int callDirection,
int callerNumberVerificationStatus,
String contactDisplayName,
- String activeChildCallId
+ String activeChildCallId,
+ CallEndpoint activeCallEndpoint,
+ Set<CallEndpoint> availableCallEndpoints
) {
mId = id;
mState = state;
@@ -390,6 +425,8 @@ public final class ParcelableCall implements Parcelable {
mCallerNumberVerificationStatus = callerNumberVerificationStatus;
mContactDisplayName = contactDisplayName;
mActiveChildCallId = activeChildCallId;
+ mActiveCallEndpoint = activeCallEndpoint;
+ mAvailableCallEndpoints = availableCallEndpoints;
}
/** The unique ID of the call. */
@@ -614,6 +651,21 @@ public final class ParcelableCall implements Parcelable {
return mActiveChildCallId;
}
+ /**
+ * @return The {@link CallEndpoint} which is currently active for this call, or null if the call
+ * does not take place via an {@link CallEndpoint}.
+ */
+ public @Nullable CallEndpoint getActiveCallEndpoint() {
+ return mActiveCallEndpoint;
+ }
+
+ /**
+ * @return A set of available {@link CallEndpoint}
+ */
+ public @NonNull Set<CallEndpoint> getAvailableCallEndpoints() {
+ return mAvailableCallEndpoints;
+ }
+
/** Responsible for creating ParcelableCall objects for deserialized Parcels. */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public static final @android.annotation.NonNull Parcelable.Creator<ParcelableCall> CREATOR =
@@ -623,9 +675,9 @@ public final class ParcelableCall implements Parcelable {
ClassLoader classLoader = ParcelableCall.class.getClassLoader();
String id = source.readString();
int state = source.readInt();
- DisconnectCause disconnectCause = source.readParcelable(classLoader);
+ DisconnectCause disconnectCause = source.readParcelable(classLoader, android.telecom.DisconnectCause.class);
List<String> cannedSmsResponses = new ArrayList<>();
- source.readList(cannedSmsResponses, classLoader);
+ source.readList(cannedSmsResponses, classLoader, java.lang.String.class);
int capabilities = source.readInt();
int properties = source.readInt();
long connectTimeMillis = source.readLong();
@@ -633,28 +685,31 @@ public final class ParcelableCall implements Parcelable {
int handlePresentation = source.readInt();
String callerDisplayName = source.readString();
int callerDisplayNamePresentation = source.readInt();
- GatewayInfo gatewayInfo = source.readParcelable(classLoader);
- PhoneAccountHandle accountHandle = source.readParcelable(classLoader);
+ GatewayInfo gatewayInfo = source.readParcelable(classLoader, android.telecom.GatewayInfo.class);
+ PhoneAccountHandle accountHandle = source.readParcelable(classLoader, android.telecom.PhoneAccountHandle.class);
boolean isVideoCallProviderChanged = source.readByte() == 1;
IVideoProvider videoCallProvider =
IVideoProvider.Stub.asInterface(source.readStrongBinder());
String parentCallId = source.readString();
List<String> childCallIds = new ArrayList<>();
- source.readList(childCallIds, classLoader);
- StatusHints statusHints = source.readParcelable(classLoader);
+ source.readList(childCallIds, classLoader, java.lang.String.class);
+ StatusHints statusHints = source.readParcelable(classLoader, android.telecom.StatusHints.class);
int videoState = source.readInt();
List<String> conferenceableCallIds = new ArrayList<>();
- source.readList(conferenceableCallIds, classLoader);
+ source.readList(conferenceableCallIds, classLoader, java.lang.String.class);
Bundle intentExtras = source.readBundle(classLoader);
Bundle extras = source.readBundle(classLoader);
int supportedAudioRoutes = source.readInt();
boolean isRttCallChanged = source.readByte() == 1;
- ParcelableRttCall rttCall = source.readParcelable(classLoader);
+ ParcelableRttCall rttCall = source.readParcelable(classLoader, android.telecom.ParcelableRttCall.class);
long creationTimeMillis = source.readLong();
int callDirection = source.readInt();
int callerNumberVerificationStatus = source.readInt();
String contactDisplayName = source.readString();
String activeChildCallId = source.readString();
+ CallEndpoint activeCallEndpoint = source.readParcelable(classLoader);
+ List<CallEndpoint> availablableCallEndpoints = new ArrayList<>();
+ source.readList(availablableCallEndpoints, classLoader);
return new ParcelableCallBuilder()
.setId(id)
.setState(state)
@@ -686,6 +741,8 @@ public final class ParcelableCall implements Parcelable {
.setCallerNumberVerificationStatus(callerNumberVerificationStatus)
.setContactDisplayName(contactDisplayName)
.setActiveChildCallId(activeChildCallId)
+ .setActiveCallEndpoint(activeCallEndpoint)
+ .setAvailableCallEndpoints(new HashSet<>(availablableCallEndpoints))
.createParcelableCall();
}
@@ -735,6 +792,8 @@ public final class ParcelableCall implements Parcelable {
destination.writeInt(mCallerNumberVerificationStatus);
destination.writeString(mContactDisplayName);
destination.writeString(mActiveChildCallId);
+ destination.writeParcelable(mActiveCallEndpoint, 0);
+ destination.writeList(Arrays.asList(mAvailableCallEndpoints.toArray()));
}
@Override
diff --git a/telecomm/java/android/telecom/ParcelableConference.java b/telecomm/java/android/telecom/ParcelableConference.java
index 1f8aafbca476..e57c833e930e 100644
--- a/telecomm/java/android/telecom/ParcelableConference.java
+++ b/telecomm/java/android/telecom/ParcelableConference.java
@@ -292,24 +292,24 @@ public final class ParcelableConference implements Parcelable {
@Override
public ParcelableConference createFromParcel(Parcel source) {
ClassLoader classLoader = ParcelableConference.class.getClassLoader();
- PhoneAccountHandle phoneAccount = source.readParcelable(classLoader);
+ PhoneAccountHandle phoneAccount = source.readParcelable(classLoader, android.telecom.PhoneAccountHandle.class);
int state = source.readInt();
int capabilities = source.readInt();
List<String> connectionIds = new ArrayList<>(2);
- source.readList(connectionIds, classLoader);
+ source.readList(connectionIds, classLoader, java.lang.String.class);
long connectTimeMillis = source.readLong();
IVideoProvider videoCallProvider =
IVideoProvider.Stub.asInterface(source.readStrongBinder());
int videoState = source.readInt();
- StatusHints statusHints = source.readParcelable(classLoader);
+ StatusHints statusHints = source.readParcelable(classLoader, android.telecom.StatusHints.class);
Bundle extras = source.readBundle(classLoader);
int properties = source.readInt();
long connectElapsedTimeMillis = source.readLong();
- Uri address = source.readParcelable(classLoader);
+ Uri address = source.readParcelable(classLoader, android.net.Uri.class);
int addressPresentation = source.readInt();
String callerDisplayName = source.readString();
int callerDisplayNamePresentation = source.readInt();
- DisconnectCause disconnectCause = source.readParcelable(classLoader);
+ DisconnectCause disconnectCause = source.readParcelable(classLoader, android.telecom.DisconnectCause.class);
boolean isRingbackRequested = source.readInt() == 1;
int callDirection = source.readInt();
diff --git a/telecomm/java/android/telecom/ParcelableConnection.java b/telecomm/java/android/telecom/ParcelableConnection.java
index 2b9ce9b46ad7..7b8333870eaf 100644
--- a/telecomm/java/android/telecom/ParcelableConnection.java
+++ b/telecomm/java/android/telecom/ParcelableConnection.java
@@ -261,10 +261,10 @@ public final class ParcelableConnection implements Parcelable {
public ParcelableConnection createFromParcel(Parcel source) {
ClassLoader classLoader = ParcelableConnection.class.getClassLoader();
- PhoneAccountHandle phoneAccount = source.readParcelable(classLoader);
+ PhoneAccountHandle phoneAccount = source.readParcelable(classLoader, android.telecom.PhoneAccountHandle.class);
int state = source.readInt();
int capabilities = source.readInt();
- Uri address = source.readParcelable(classLoader);
+ Uri address = source.readParcelable(classLoader, android.net.Uri.class);
int addressPresentation = source.readInt();
String callerDisplayName = source.readString();
int callerDisplayNamePresentation = source.readInt();
@@ -274,8 +274,8 @@ public final class ParcelableConnection implements Parcelable {
boolean ringbackRequested = source.readByte() == 1;
boolean audioModeIsVoip = source.readByte() == 1;
long connectTimeMillis = source.readLong();
- StatusHints statusHints = source.readParcelable(classLoader);
- DisconnectCause disconnectCause = source.readParcelable(classLoader);
+ StatusHints statusHints = source.readParcelable(classLoader, android.telecom.StatusHints.class);
+ DisconnectCause disconnectCause = source.readParcelable(classLoader, android.telecom.DisconnectCause.class);
List<String> conferenceableConnectionIds = new ArrayList<>();
source.readStringList(conferenceableConnectionIds);
Bundle extras = Bundle.setDefusable(source.readBundle(classLoader), true);
diff --git a/telecomm/java/android/telecom/ParcelableRttCall.java b/telecomm/java/android/telecom/ParcelableRttCall.java
index fbcf486151f9..b88473a8a63b 100644
--- a/telecomm/java/android/telecom/ParcelableRttCall.java
+++ b/telecomm/java/android/telecom/ParcelableRttCall.java
@@ -46,8 +46,8 @@ public class ParcelableRttCall implements Parcelable {
protected ParcelableRttCall(Parcel in) {
mRttMode = in.readInt();
- mTransmitStream = in.readParcelable(ParcelFileDescriptor.class.getClassLoader());
- mReceiveStream = in.readParcelable(ParcelFileDescriptor.class.getClassLoader());
+ mTransmitStream = in.readParcelable(ParcelFileDescriptor.class.getClassLoader(), android.os.ParcelFileDescriptor.class);
+ mReceiveStream = in.readParcelable(ParcelFileDescriptor.class.getClassLoader(), android.os.ParcelFileDescriptor.class);
}
public static final @android.annotation.NonNull Creator<ParcelableRttCall> CREATOR = new Creator<ParcelableRttCall>() {
diff --git a/telecomm/java/android/telecom/Phone.java b/telecomm/java/android/telecom/Phone.java
index bc0a14667307..ac91a926e267 100644
--- a/telecomm/java/android/telecom/Phone.java
+++ b/telecomm/java/android/telecom/Phone.java
@@ -292,6 +292,29 @@ public final class Phone {
}
}
+ void internalOnCallPullFailed(String callId, @Call.Callback.PullFailedReason int reason) {
+ Call call = getCallById(callId);
+ if (call != null) {
+ call.internalOnCallPullFailed(reason);
+ }
+ }
+
+ void internalOnAnswerFailed(String callId, CallEndpoint callEndpoint,
+ @Call.Callback.AnswerFailedReason int reason) {
+ Call call = getCallById(callId);
+ if (call != null) {
+ call.internalOnAnswerFailed(callEndpoint, reason);
+ }
+ }
+
+ void internalOnCallPushFailed(String callId, CallEndpoint callEndpoint,
+ @Call.Callback.PushFailedReason int reason) {
+ Call call = getCallById(callId);
+ if (call != null) {
+ call.internalOnCallPushFailed(callEndpoint, reason);
+ }
+ }
+
/**
* Called to destroy the phone and cleanup any lingering calls.
*/
diff --git a/telecomm/java/android/telecom/PhoneAccountSuggestion.java b/telecomm/java/android/telecom/PhoneAccountSuggestion.java
index 2589d9504f6d..d9f89d544f40 100644
--- a/telecomm/java/android/telecom/PhoneAccountSuggestion.java
+++ b/telecomm/java/android/telecom/PhoneAccountSuggestion.java
@@ -84,7 +84,7 @@ public final class PhoneAccountSuggestion implements Parcelable {
}
private PhoneAccountSuggestion(Parcel in) {
- mHandle = in.readParcelable(PhoneAccountHandle.class.getClassLoader());
+ mHandle = in.readParcelable(PhoneAccountHandle.class.getClassLoader(), android.telecom.PhoneAccountHandle.class);
mReason = in.readInt();
mShouldAutoSelect = in.readByte() != 0;
}
diff --git a/telecomm/java/android/telecom/StatusHints.java b/telecomm/java/android/telecom/StatusHints.java
index 762c93a49022..2faecc2e3468 100644
--- a/telecomm/java/android/telecom/StatusHints.java
+++ b/telecomm/java/android/telecom/StatusHints.java
@@ -132,8 +132,8 @@ public final class StatusHints implements Parcelable {
private StatusHints(Parcel in) {
mLabel = in.readCharSequence();
- mIcon = in.readParcelable(getClass().getClassLoader());
- mExtras = in.readParcelable(getClass().getClassLoader());
+ mIcon = in.readParcelable(getClass().getClassLoader(), android.graphics.drawable.Icon.class);
+ mExtras = in.readParcelable(getClass().getClassLoader(), android.os.Bundle.class);
}
@Override
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 6279bf88ab1c..e560f34cfcd8 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -54,8 +54,10 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
import java.util.Objects;
+import java.util.Set;
import java.util.concurrent.Executor;
/**
@@ -586,6 +588,14 @@ public class TelecomManager {
"android.telecom.extra.START_CALL_WITH_RTT";
/**
+ * A parcelable extra, which when set on the bundle passed into {@link #placeCall(Uri, Bundle)},
+ * indicates that the call should be initiated with an active {@link CallEndpoint} to stream
+ * the call as a tethered call.
+ */
+ public static final String EXTRA_START_CALL_ON_ENDPOINT =
+ "android.telecom.extra.START_CALL_ON_ENDPOINT";
+
+ /**
* Start an activity indicating that the completion of an outgoing call or an incoming call
* which was not blocked by the {@link CallScreeningService}, and which was NOT terminated
* while the call was in {@link Call#STATE_AUDIO_PROCESSING}.
@@ -745,6 +755,23 @@ public class TelecomManager {
"android.telecom.INCLUDE_SELF_MANAGED_CALLS";
/**
+ * A boolean meta-data value indicating this {@link InCallService} implementation is aimed at
+ * working as a streaming app for a tethered call. When there's a tethered call
+ * requesting to a {@link CallEndpoint} registered with this app, Telecom will bind to this
+ * streaming app and let the app streaming the call to the requested endpoint.
+ * <p>
+ * This meta-data can only be set for an {@link InCallService} which doesn't set neither
+ * {@link #METADATA_IN_CALL_SERVICE_UI} nor {@link #METADATA_IN_CALL_SERVICE_CAR_MODE_UI}.
+ * Otherwise, the app will be treated as a phone/dialer app or a car-mode app.
+ * <p>
+ * The {@link InCallService} declared this meta-data must implement
+ * {@link InCallService#onCallEndpointActivationRequested(CallEndpoint, CallEndpointSession)}.
+ * See this method for more information.
+ */
+ public static final String METADATA_STREAMING_TETHERED_CALLS =
+ "android.telecom.STREAMING_TETHERED_CALLS";
+
+ /**
* The dual tone multi-frequency signaling character sent to indicate the dialing system should
* pause for a predefined period.
*/
@@ -1294,14 +1321,24 @@ public class TelecomManager {
* {@link PhoneAccount#CAPABILITY_SELF_MANAGED}.
* <p>
* Requires permission {@link android.Manifest.permission#READ_PHONE_STATE}, or that the caller
- * is the default dialer app.
+ * is the default dialer app to get all phone account handles.
+ * <P>
+ * If the caller doesn't meet any of the above requirements and has {@link
+ * android.Manifest.permission#MANAGE_OWN_CALLS}, the caller can get only the phone account
+ * handles they have registered.
* <p>
- * A {@link SecurityException} will be thrown if a called is not the default dialer, or lacks
- * the {@link android.Manifest.permission#READ_PHONE_STATE} permission.
+ * A {@link SecurityException} will be thrown if the caller is not the default dialer
+ * or the caller does not have at least one of the following permissions:
+ * {@link android.Manifest.permission#READ_PHONE_STATE} permission,
+ * {@link android.Manifest.permission#MANAGE_OWN_CALLS} permission
*
* @return A list of {@code PhoneAccountHandle} objects.
*/
- @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ @RequiresPermission(anyOf = {
+ READ_PRIVILEGED_PHONE_STATE,
+ android.Manifest.permission.READ_PHONE_STATE,
+ android.Manifest.permission.MANAGE_OWN_CALLS
+ })
public List<PhoneAccountHandle> getSelfManagedPhoneAccounts() {
ITelecomService service = getTelecomService();
if (service != null) {
@@ -2250,6 +2287,7 @@ public class TelecomManager {
* <li>{@link #EXTRA_PHONE_ACCOUNT_HANDLE}</li>
* <li>{@link #EXTRA_START_CALL_WITH_SPEAKERPHONE}</li>
* <li>{@link #EXTRA_START_CALL_WITH_VIDEO_STATE}</li>
+ * <li>{@link #EXTRA_START_CALL_ON_ENDPOINT}</li>
* </ul>
* <p>
* An app which implements the self-managed {@link ConnectionService} API uses
@@ -2579,6 +2617,79 @@ public class TelecomManager {
}
}
+ /**
+ * Register a set of {@link CallEndpoint} to telecom. All registered {@link CallEndpoint} can
+ * be provided as options for push, place or answer call externally.
+ *
+ * @param endpoints Endpoints to be registered.
+ */
+ // TODO: add permission requirements
+ // @RequiresPermission{}
+ public void registerCallEndpoints(@NonNull Set<CallEndpoint> endpoints) {
+ ITelecomService service = getTelecomService();
+ List<CallEndpoint> endpointList = new ArrayList<>(endpoints);
+ if (service != null) {
+ try {
+ service.registerCallEndpoints(endpointList, mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException registerCallEndpoints: " + e);
+ e.rethrowAsRuntimeException();
+ }
+ } else {
+ throw new IllegalStateException("Telecom service is null.");
+ }
+ }
+
+ /**
+ * Unregister all {@link CallEndpoint} from telecom in the set provided. After un-registration,
+ * telecom will stop tracking and maintaining these {@link CallEndpoint}, user can no longer
+ * carry a call on them.
+ *
+ * @param endpoints
+ */
+ // TODO: add permission requirements
+ // @RequiresPermission{}
+ public void unregisterCallEndpoints(@NonNull Set<CallEndpoint> endpoints) {
+ ITelecomService service = getTelecomService();
+ List<CallEndpoint> endpointList = new ArrayList<>(endpoints);
+ if (service != null) {
+ try {
+ service.unregisterCallEndpoints(endpointList, mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException unregisterCallEndpoints: " + e);
+ e.rethrowAsRuntimeException();
+ }
+ } else {
+ throw new IllegalStateException("Telecom service is null.");
+ }
+ }
+
+ /**
+ * Return a set all registered {@link CallEndpoint} that can be used to stream and carry an
+ * external call.
+ *
+ * @return A set of all available {@link CallEndpoint}.
+ */
+ // TODO: add permission requirements
+ // @RequiresPermission{}
+ public @NonNull Set<CallEndpoint> getCallEndpoints() {
+ Set<CallEndpoint> endpoints = new HashSet<>();
+ List<CallEndpoint> endpointList;
+ ITelecomService service = getTelecomService();
+ if (service != null) {
+ try {
+ endpointList = service.getCallEndpoints(mContext.getOpPackageName());
+ return new HashSet<>(endpointList);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException registerCallEndpoints: " + e);
+ e.rethrowAsRuntimeException();
+ }
+ } else {
+ throw new IllegalStateException("Telecom service is null.");
+ }
+ return endpoints;
+ }
+
private boolean isSystemProcess() {
return Process.myUid() == Process.SYSTEM_UID;
}
diff --git a/telecomm/java/com/android/internal/telecom/ICallEndpointCallback.aidl b/telecomm/java/com/android/internal/telecom/ICallEndpointCallback.aidl
new file mode 100644
index 000000000000..dc1cc0f5a3c9
--- /dev/null
+++ b/telecomm/java/com/android/internal/telecom/ICallEndpointCallback.aidl
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telecom;
+
+/**
+ * Internal remote CallEndpointCallback interface for Telecom framework to report event related to
+ * the endpoint session.
+ *
+ * {@hide}
+ */
+oneway interface ICallEndpointCallback {
+ void onCallEndpointSessionActivationTimeout();
+
+ void onCallEndpointSessionDeactivated();
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackPreviewHandler.java b/telecomm/java/com/android/internal/telecom/ICallEndpointSession.aidl
index dc20f7b72a2b..1c1c29a22f29 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackPreviewHandler.java
+++ b/telecomm/java/com/android/internal/telecom/ICallEndpointSession.aidl
@@ -14,28 +14,21 @@
* limitations under the License.
*/
-package com.android.wm.shell.back;
-
-import android.os.SystemProperties;
-import android.view.IWindowManager;
-
-import javax.inject.Inject;
+package com.android.internal.telecom;
/**
- * Handle the preview of what a back gesture will lead to.
+ * Internal remote CallEndpointSession interface for streaming app to update the status of the
+ * endpoint.
+ *
+ * @see android.telecom.CallEndpointSession
+ *
+ * {@hide}
*/
-public class BackPreviewHandler {
-
- private static final String BACK_PREDICTABILITY_PROP = "persist.debug.back_predictability";
- public static boolean isEnabled() {
- return SystemProperties.getInt(BACK_PREDICTABILITY_PROP, 0) > 0;
- }
+oneway interface ICallEndpointSession {
+ void setCallEndpointSessionActivated();
- private final IWindowManager mWmService;
+ void setCallEndpointSessionActivationFailed(int reason);
- @Inject
- public BackPreviewHandler(IWindowManager windowManagerService) {
- mWmService = windowManagerService;
- }
-}
+ void setCallEndpointSessionDeactivated();
+} \ No newline at end of file
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
index d72f8aa82ddb..986871fd0377 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
@@ -20,6 +20,7 @@ import android.net.Uri;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.telecom.CallAudioState;
+import android.telecom.CallEndpoint;
import android.telecom.Connection;
import android.telecom.ConnectionRequest;
import android.telecom.Logging.Session;
diff --git a/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl b/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
index edf1cf4cdb18..ecca835a45b2 100755
--- a/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
@@ -18,6 +18,7 @@ package com.android.internal.telecom;
import android.net.Uri;
import android.os.Bundle;
+import android.telecom.CallEndpoint;
import android.telecom.PhoneAccountHandle;
/**
@@ -95,4 +96,8 @@ oneway interface IInCallAdapter {
void handoverTo(String callId, in PhoneAccountHandle destAcct, int videoState,
in Bundle extras);
+
+ void pushCall(String callId, in CallEndpoint endpoint);
+
+ void answerCallViaEndpoint(String callId, in CallEndpoint endpoint, int videoState);
}
diff --git a/telecomm/java/com/android/internal/telecom/IInCallService.aidl b/telecomm/java/com/android/internal/telecom/IInCallService.aidl
index b9563fa7bb18..93d9f282560f 100644
--- a/telecomm/java/com/android/internal/telecom/IInCallService.aidl
+++ b/telecomm/java/com/android/internal/telecom/IInCallService.aidl
@@ -19,9 +19,12 @@ package com.android.internal.telecom;
import android.app.PendingIntent;
import android.os.Bundle;
import android.telecom.CallAudioState;
+import android.telecom.CallEndpoint;
import android.telecom.ParcelableCall;
import com.android.internal.telecom.IInCallAdapter;
+import com.android.internal.telecom.ICallEndpointCallback;
+import com.android.internal.telecom.ICallEndpointSession;
/**
* Internal remote interface for in-call services.
@@ -30,9 +33,12 @@ import com.android.internal.telecom.IInCallAdapter;
*
* {@hide}
*/
-oneway interface IInCallService {
+interface IInCallService {
void setInCallAdapter(in IInCallAdapter inCallAdapter);
+ ICallEndpointCallback requestCallEndpointActivation(in CallEndpoint callEndpoint,
+ in ICallEndpointSession callEndpointSession);
+
void addCall(in ParcelableCall call);
void updateCall(in ParcelableCall call);
@@ -58,4 +64,10 @@ oneway interface IInCallService {
void onHandoverFailed(String callId, int error);
void onHandoverComplete(String callId);
+
+ void onCallPullFailed(String callId, int reason);
+
+ void onCallPushFailed(String callId, in CallEndpoint endpoint, int reason);
+
+ void onAnswerFailed(String callId, in CallEndpoint endpoint, int reason);
}
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index a75f79caeee0..985f6bc7131b 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -18,6 +18,7 @@ package com.android.internal.telecom;
import android.content.ComponentName;
import android.content.Intent;
+import android.telecom.CallEndpoint;
import android.telecom.TelecomAnalytics;
import android.telecom.PhoneAccountHandle;
import android.net.Uri;
@@ -342,6 +343,8 @@ interface ITelecomService {
void cleanupStuckCalls();
+ int cleanupOrphanPhoneAccounts();
+
void resetCarMode();
void setTestDefaultCallRedirectionApp(String packageName);
@@ -366,4 +369,19 @@ interface ITelecomService {
* @see TelecomServiceImpl#setTestCallDiagnosticService
*/
void setTestCallDiagnosticService(in String packageName);
+
+ /**
+ * @see TelecomServiceImpl#registerCallEndpoints(in List<CallEndpoint>, in String);
+ */
+ void registerCallEndpoints(in List<CallEndpoint> endpoints, in String packageName);
+
+ /**
+ * @see TelecomServiceImpl#unregisterCallEndpoints(in List<CallEndpoint>, String);
+ */
+ void unregisterCallEndpoints(in List<CallEndpoint> endpoints, in String packageName);
+
+ /**
+ * @see TelecomServiceImpl#getCallEndpoints(in String packageName);
+ */
+ List<CallEndpoint> getCallEndpoints(in String packageName);
}
diff --git a/telephony/java/android/telephony/AvailableNetworkInfo.java b/telephony/java/android/telephony/AvailableNetworkInfo.java
index 2b355ae216e3..6d673fbc7305 100644
--- a/telephony/java/android/telephony/AvailableNetworkInfo.java
+++ b/telephony/java/android/telephony/AvailableNetworkInfo.java
@@ -185,9 +185,9 @@ public final class AvailableNetworkInfo implements Parcelable {
mMccMncs = new ArrayList<>();
in.readStringList(mMccMncs);
mBands = new ArrayList<>();
- in.readList(mBands, Integer.class.getClassLoader());
+ in.readList(mBands, Integer.class.getClassLoader(), java.lang.Integer.class);
mRadioAccessSpecifiers = new ArrayList<>();
- in.readList(mRadioAccessSpecifiers, RadioAccessSpecifier.class.getClassLoader());
+ in.readList(mRadioAccessSpecifiers, RadioAccessSpecifier.class.getClassLoader(), android.telephony.RadioAccessSpecifier.class);
}
public AvailableNetworkInfo(int subId, int priority, @NonNull List<String> mccMncs,
diff --git a/telephony/java/android/telephony/BarringInfo.java b/telephony/java/android/telephony/BarringInfo.java
index 0aa4b5805cd6..29152f19d17d 100644
--- a/telephony/java/android/telephony/BarringInfo.java
+++ b/telephony/java/android/telephony/BarringInfo.java
@@ -294,8 +294,8 @@ public final class BarringInfo implements Parcelable {
/** @hide */
public BarringInfo(Parcel p) {
- mCellIdentity = p.readParcelable(CellIdentity.class.getClassLoader());
- mBarringServiceInfos = p.readSparseArray(BarringServiceInfo.class.getClassLoader());
+ mCellIdentity = p.readParcelable(CellIdentity.class.getClassLoader(), android.telephony.CellIdentity.class);
+ mBarringServiceInfos = p.readSparseArray(BarringServiceInfo.class.getClassLoader(), android.telephony.BarringInfo.BarringServiceInfo.class);
}
@Override
diff --git a/telephony/java/android/telephony/CallAttributes.java b/telephony/java/android/telephony/CallAttributes.java
index 0c258f4b6435..b7bef39aa275 100644
--- a/telephony/java/android/telephony/CallAttributes.java
+++ b/telephony/java/android/telephony/CallAttributes.java
@@ -53,9 +53,9 @@ public final class CallAttributes implements Parcelable {
}
private CallAttributes(Parcel in) {
- this.mPreciseCallState = in.readParcelable(PreciseCallState.class.getClassLoader());
+ this.mPreciseCallState = in.readParcelable(PreciseCallState.class.getClassLoader(), android.telephony.PreciseCallState.class);
this.mNetworkType = in.readInt();
- this.mCallQuality = in.readParcelable(CallQuality.class.getClassLoader());
+ this.mCallQuality = in.readParcelable(CallQuality.class.getClassLoader(), android.telephony.CallQuality.class);
}
// getters
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 21967f4f4687..d5c846d5ed81 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -175,7 +175,10 @@ public class CarrierConfigManager {
/**
* This flag specifies whether VoLTE availability is based on provisioning. By default this is
* false.
+ * Used for UCE to determine if EAB provisioning checks should be based on provisioning.
+ * @deprecated Use {@link Ims#KEY_MMTEL_REQUIRES_PROVISIONING_BUNDLE} instead.
*/
+ @Deprecated
public static final String
KEY_CARRIER_VOLTE_PROVISIONED_BOOL = "carrier_volte_provisioned_bool";
@@ -879,7 +882,12 @@ public class CarrierConfigManager {
/**
* Flag specifying whether provisioning is required for VoLTE, Video Telephony, and WiFi
* Calling.
+
+ * Combines VoLTE, VT, VoWiFI calling provisioning into one parameter.
+ * @deprecated Use {@link Ims#KEY_MMTEL_REQUIRES_PROVISIONING_BUNDLE} instead for
+ * finer-grained control.
*/
+ @Deprecated
public static final String KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL
= "carrier_volte_provisioning_required_bool";
@@ -893,7 +901,11 @@ public class CarrierConfigManager {
* and enable the UT over IMS capability for the subscription when the subscription is loaded.
*
* The default value for this key is {@code false}.
+ *
+ * @deprecated Use {@link Ims#KEY_MMTEL_REQUIRES_PROVISIONING_BUNDLE} instead for
+ * determining if UT requires provisioning.
*/
+ @Deprecated
public static final String KEY_CARRIER_UT_PROVISIONING_REQUIRED_BOOL =
"carrier_ut_provisioning_required_bool";
@@ -5173,6 +5185,95 @@ public class CarrierConfigManager {
/** E911 RTP inactivity occurred when call is connected. */
public static final int E911_RTP_INACTIVITY_ON_CONNECTED = 4;
+ /**
+ * A bundle which specifies the MMTEL capability and registration technology
+ * that requires provisioning. If a tuple is not present, the
+ * framework will not require that the tuple requires provisioning before
+ * enabling the capability.
+ * <p> Possible keys in this bundle are
+ * <ul>
+ * <li>{@link #KEY_CAPABILITY_TYPE_VOICE_INT_ARRAY}</li>
+ * <li>{@link #KEY_CAPABILITY_TYPE_VIDEO_INT_ARRAY}</li>
+ * <li>{@link #KEY_CAPABILITY_TYPE_UT_INT_ARRAY}</li>
+ * <li>{@link #KEY_CAPABILITY_TYPE_SMS_INT_ARRAY}</li>
+ * <li>{@link #KEY_CAPABILITY_CALL_COMPOSER_INT_ARRAY}</li>
+ * </ul>
+ * <p> The values are defined in
+ * {@link android.telephony.ims.stub.ImsRegistrationImplBase.ImsRegistrationTech}
+ */
+ public static final String KEY_MMTEL_REQUIRES_PROVISIONING_BUNDLE =
+ KEY_PREFIX + "mmtel_requires_provisioning_bundle";
+
+ /**
+ * This MmTelFeature supports Voice calling (IR.92)
+ * @see MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE
+ */
+ public static final String KEY_CAPABILITY_TYPE_VOICE_INT_ARRAY =
+ KEY_PREFIX + "key_capability_type_voice_int_array";
+
+ /**
+ * This MmTelFeature supports Video (IR.94)
+ * @see MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO
+ */
+ public static final String KEY_CAPABILITY_TYPE_VIDEO_INT_ARRAY =
+ KEY_PREFIX + "key_capability_type_video_int_array";
+
+ /**
+ * This MmTelFeature supports XCAP over Ut for supplementary services. (IR.92)
+ * @see MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_UT
+ */
+ public static final String KEY_CAPABILITY_TYPE_UT_INT_ARRAY =
+ KEY_PREFIX + "key_capability_type_ut_int_array";
+
+ /**
+ * This MmTelFeature supports SMS (IR.92)
+ * @see MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_SMS
+ */
+ public static final String KEY_CAPABILITY_TYPE_SMS_INT_ARRAY =
+ KEY_PREFIX + "key_capability_type_sms_int_array";
+
+ /**
+ * This MmTelFeature supports Call Composer (section 2.4 of RCC.20)
+ * @see MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_CALL_COMPOSER
+ */
+ public static final String KEY_CAPABILITY_CALL_COMPOSER_INT_ARRAY =
+ KEY_PREFIX + "key_capability_type_call_composer_int_array";
+
+ /**
+ * A bundle which specifies the RCS capability and registration technology
+ * that requires provisioning. If a tuple is not present, the
+ * framework will not require that the tuple requires provisioning before
+ * enabling the capability.
+ * <p> Possible keys in this bundle are
+ * <ul>
+ * <li>{@link #KEY_CAPABILITY_TYPE_OPTIONS_UCE_INT_ARRAY}</li>
+ * <li>{@link #KEY_CAPABILITY_TYPE_PRESENCE_UCE_INT_ARRAY}</li>
+ * </ul>
+ * <p> The values are defined in
+ * {@link android.telephony.ims.stub.ImsRegistrationImplBase.ImsRegistrationTech}
+ */
+ public static final String KEY_RCS_REQUIRES_PROVISIONING_BUNDLE =
+ KEY_PREFIX + "rcs_requires_provisioning_bundle";
+
+ /**
+ * This carrier supports User Capability Exchange using SIP OPTIONS as defined by the
+ * framework. If set, the RcsFeature should support capability exchange using SIP OPTIONS.
+ * If not set, this RcsFeature should not service capability requests.
+ * @see RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_OPTIONS_UCE
+ */
+ public static final String KEY_CAPABILITY_TYPE_OPTIONS_UCE_INT_ARRAY =
+ KEY_PREFIX + "key_capability_type_options_uce_int_array";
+
+ /**
+ * This carrier supports User Capability Exchange using a presence server as defined by the
+ * framework. If set, the RcsFeature should support capability exchange using a presence
+ * server. If not set, this RcsFeature should not publish capabilities or service capability
+ * requests using presence.
+ * @see RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_PRESENCE_UCE
+ */
+ public static final String KEY_CAPABILITY_TYPE_PRESENCE_UCE_INT_ARRAY =
+ KEY_PREFIX + "key_capability_type_presence_uce_int_array";
+
private Ims() {}
private static PersistableBundle getDefaults() {
@@ -5209,6 +5310,20 @@ public class CarrierConfigManager {
"+g.gsma.rcs.botversion=\"#=1,#=2\"",
"+g.gsma.rcs.cpimext"});
+ /**
+ * @see #KEY_MMTEL_REQUIRES_PROVISIONING_BUNDLE
+ */
+ PersistableBundle mmtel_requires_provisioning_int_array = new PersistableBundle();
+ defaults.putPersistableBundle(
+ KEY_MMTEL_REQUIRES_PROVISIONING_BUNDLE, mmtel_requires_provisioning_int_array);
+
+ /**
+ * @see #KEY_RCS_REQUIRES_PROVISIONING_BUNDLE
+ */
+ PersistableBundle rcs_requires_provisioning_int_array = new PersistableBundle();
+ defaults.putPersistableBundle(
+ KEY_RCS_REQUIRES_PROVISIONING_BUNDLE, rcs_requires_provisioning_int_array);
+
defaults.putBoolean(KEY_GRUU_ENABLED_BOOL, true);
defaults.putBoolean(KEY_SIP_OVER_IPSEC_ENABLED_BOOL, true);
defaults.putBoolean(KEY_KEEP_PDN_UP_IN_NO_VOPS_BOOL, false);
@@ -7573,8 +7688,8 @@ public class CarrierConfigManager {
public static final String KEY_EPDG_PCO_ID_IPV4_INT = KEY_PREFIX + "epdg_pco_id_ipv4_int";
/** Controls if the IKE tunnel setup supports EAP-AKA fast reauth */
- public static final String KEY_ENABLE_SUPPORT_FOR_EAP_AKA_FAST_REAUTH_BOOL =
- KEY_PREFIX + "enable_support_for_eap_aka_fast_reauth_bool";
+ public static final String KEY_SUPPORTS_EAP_AKA_FAST_REAUTH_BOOL =
+ KEY_PREFIX + "supports_eap_aka_fast_reauth_bool";
/** @hide */
@IntDef({AUTHENTICATION_METHOD_EAP_ONLY, AUTHENTICATION_METHOD_CERT})
@@ -7722,7 +7837,7 @@ public class CarrierConfigManager {
defaults.putBoolean(KEY_ADD_KE_TO_CHILD_SESSION_REKEY_BOOL, false);
defaults.putInt(KEY_EPDG_PCO_ID_IPV6_INT, 0);
defaults.putInt(KEY_EPDG_PCO_ID_IPV4_INT, 0);
- defaults.putBoolean(KEY_ENABLE_SUPPORT_FOR_EAP_AKA_FAST_REAUTH_BOOL, false);
+ defaults.putBoolean(KEY_SUPPORTS_EAP_AKA_FAST_REAUTH_BOOL, false);
return defaults;
}
diff --git a/telephony/java/android/telephony/CellIdentityLte.java b/telephony/java/android/telephony/CellIdentityLte.java
index 4db00cf258e5..b4b8aee31b54 100644
--- a/telephony/java/android/telephony/CellIdentityLte.java
+++ b/telephony/java/android/telephony/CellIdentityLte.java
@@ -379,7 +379,7 @@ public final class CellIdentityLte extends CellIdentity {
mBands = in.createIntArray();
mBandwidth = in.readInt();
mAdditionalPlmns = (ArraySet<String>) in.readArraySet(null);
- mCsgInfo = in.readParcelable(null);
+ mCsgInfo = in.readParcelable(null, android.telephony.ClosedSubscriberGroupInfo.class);
updateGlobalCellId();
if (DBG) log(toString());
diff --git a/telephony/java/android/telephony/CellIdentityTdscdma.java b/telephony/java/android/telephony/CellIdentityTdscdma.java
index 13d93737f751..90e6295abda8 100644
--- a/telephony/java/android/telephony/CellIdentityTdscdma.java
+++ b/telephony/java/android/telephony/CellIdentityTdscdma.java
@@ -297,7 +297,7 @@ public final class CellIdentityTdscdma extends CellIdentity {
mCpid = in.readInt();
mUarfcn = in.readInt();
mAdditionalPlmns = (ArraySet<String>) in.readArraySet(null);
- mCsgInfo = in.readParcelable(null);
+ mCsgInfo = in.readParcelable(null, android.telephony.ClosedSubscriberGroupInfo.class);
updateGlobalCellId();
if (DBG) log(toString());
diff --git a/telephony/java/android/telephony/CellIdentityWcdma.java b/telephony/java/android/telephony/CellIdentityWcdma.java
index 9b463da14f16..72282cdb344b 100644
--- a/telephony/java/android/telephony/CellIdentityWcdma.java
+++ b/telephony/java/android/telephony/CellIdentityWcdma.java
@@ -313,7 +313,7 @@ public final class CellIdentityWcdma extends CellIdentity {
mPsc = in.readInt();
mUarfcn = in.readInt();
mAdditionalPlmns = (ArraySet<String>) in.readArraySet(null);
- mCsgInfo = in.readParcelable(null);
+ mCsgInfo = in.readParcelable(null, android.telephony.ClosedSubscriberGroupInfo.class);
updateGlobalCellId();
if (DBG) log(toString());
diff --git a/telephony/java/android/telephony/CellSignalStrengthNr.java b/telephony/java/android/telephony/CellSignalStrengthNr.java
index cd22abddd3a7..f5ba3abf53a5 100644
--- a/telephony/java/android/telephony/CellSignalStrengthNr.java
+++ b/telephony/java/android/telephony/CellSignalStrengthNr.java
@@ -326,7 +326,7 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa
mCsiRsrq = in.readInt();
mCsiSinr = in.readInt();
mCsiCqiTableIndex = in.readInt();
- mCsiCqiReport = in.readArrayList(Integer.class.getClassLoader());
+ mCsiCqiReport = in.readArrayList(Integer.class.getClassLoader(), java.lang.Integer.class);
mSsRsrp = in.readInt();
mSsRsrq = in.readInt();
mSsSinr = in.readInt();
diff --git a/telephony/java/android/telephony/DataSpecificRegistrationInfo.java b/telephony/java/android/telephony/DataSpecificRegistrationInfo.java
index 957f683292f7..837124fe89de 100644
--- a/telephony/java/android/telephony/DataSpecificRegistrationInfo.java
+++ b/telephony/java/android/telephony/DataSpecificRegistrationInfo.java
@@ -105,7 +105,7 @@ public final class DataSpecificRegistrationInfo implements Parcelable {
isDcNrRestricted = source.readBoolean();
isNrAvailable = source.readBoolean();
isEnDcAvailable = source.readBoolean();
- mVopsSupportInfo = source.readParcelable(VopsSupportInfo.class.getClassLoader());
+ mVopsSupportInfo = source.readParcelable(VopsSupportInfo.class.getClassLoader(), android.telephony.VopsSupportInfo.class);
}
@Override
diff --git a/telephony/java/android/telephony/ImsManager.java b/telephony/java/android/telephony/ImsManager.java
index 2758e1273cec..b0ff9499eac4 100644
--- a/telephony/java/android/telephony/ImsManager.java
+++ b/telephony/java/android/telephony/ImsManager.java
@@ -163,6 +163,26 @@ public class ImsManager {
return new SipDelegateManager(mContext, subscriptionId, sRcsCache, sTelephonyCache);
}
+
+ /**
+ * Create an instance of {@link ProvisioningManager} for the subscription id specified.
+ * <p>
+ * Provides a ProvisioningManager instance to carrier apps to update carrier provisioning
+ * information, as well as provides a callback so that apps can listen for changes
+ * in MMTEL/RCS provisioning
+ * @param subscriptionId The ID of the subscription that this ProvisioningManager will use.
+ * @throws IllegalArgumentException if the subscription is invalid.
+ * @return a ProvisioningManager instance with the specific subscription ID.
+ */
+ @NonNull
+ public ProvisioningManager getProvisioningManager(int subscriptionId) {
+ if (!SubscriptionManager.isValidSubscriptionId(subscriptionId)) {
+ throw new IllegalArgumentException("Invalid subscription ID: " + subscriptionId);
+ }
+
+ return new ProvisioningManager(subscriptionId);
+ }
+
private static IImsRcsController getIImsRcsControllerInterface() {
return IImsRcsController.Stub.asInterface(
TelephonyFrameworkInitializer
diff --git a/telephony/java/android/telephony/NetworkRegistrationInfo.java b/telephony/java/android/telephony/NetworkRegistrationInfo.java
index 6a807665a103..c18443e81aff 100644
--- a/telephony/java/android/telephony/NetworkRegistrationInfo.java
+++ b/telephony/java/android/telephony/NetworkRegistrationInfo.java
@@ -311,12 +311,12 @@ public final class NetworkRegistrationInfo implements Parcelable {
mRejectCause = source.readInt();
mEmergencyOnly = source.readBoolean();
mAvailableServices = new ArrayList<>();
- source.readList(mAvailableServices, Integer.class.getClassLoader());
- mCellIdentity = source.readParcelable(CellIdentity.class.getClassLoader());
+ source.readList(mAvailableServices, Integer.class.getClassLoader(), java.lang.Integer.class);
+ mCellIdentity = source.readParcelable(CellIdentity.class.getClassLoader(), android.telephony.CellIdentity.class);
mVoiceSpecificInfo = source.readParcelable(
- VoiceSpecificRegistrationInfo.class.getClassLoader());
+ VoiceSpecificRegistrationInfo.class.getClassLoader(), android.telephony.VoiceSpecificRegistrationInfo.class);
mDataSpecificInfo = source.readParcelable(
- DataSpecificRegistrationInfo.class.getClassLoader());
+ DataSpecificRegistrationInfo.class.getClassLoader(), android.telephony.DataSpecificRegistrationInfo.class);
mNrState = source.readInt();
mRplmn = source.readString();
mIsUsingCarrierAggregation = source.readBoolean();
diff --git a/telephony/java/android/telephony/PcoData.java b/telephony/java/android/telephony/PcoData.java
index bcfbcf8bf5fa..39e4f2f799d8 100644
--- a/telephony/java/android/telephony/PcoData.java
+++ b/telephony/java/android/telephony/PcoData.java
@@ -19,6 +19,9 @@ package android.telephony;
import android.os.Parcel;
import android.os.Parcelable;
+import java.util.Arrays;
+import java.util.Objects;
+
/**
* Contains Carrier-specific (and opaque) Protocol configuration Option
* Data. In general this is only passed on to carrier-specific applications
@@ -84,4 +87,22 @@ public class PcoData implements Parcelable {
return "PcoData(" + cid + ", " + bearerProto + ", " + pcoId + ", contents[" +
contents.length + "])";
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ PcoData pcoData = (PcoData) o;
+ return cid == pcoData.cid
+ && pcoId == pcoData.pcoId
+ && Objects.equals(bearerProto, pcoData.bearerProto)
+ && Arrays.equals(contents, pcoData.contents);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = Objects.hash(cid, bearerProto, pcoId);
+ result = 31 * result + Arrays.hashCode(contents);
+ return result;
+ }
}
diff --git a/telephony/java/android/telephony/PhoneCapability.java b/telephony/java/android/telephony/PhoneCapability.java
index a3aaf61a6fec..63e3468ac19b 100644
--- a/telephony/java/android/telephony/PhoneCapability.java
+++ b/telephony/java/android/telephony/PhoneCapability.java
@@ -150,7 +150,7 @@ public final class PhoneCapability implements Parcelable {
mMaxActiveDataSubscriptions = in.readInt();
mNetworkValidationBeforeSwitchSupported = in.readBoolean();
mLogicalModemList = new ArrayList<>();
- in.readList(mLogicalModemList, ModemInfo.class.getClassLoader());
+ in.readList(mLogicalModemList, ModemInfo.class.getClassLoader(), android.telephony.ModemInfo.class);
mDeviceNrCapabilities = in.createIntArray();
}
diff --git a/telephony/java/android/telephony/PreciseDataConnectionState.java b/telephony/java/android/telephony/PreciseDataConnectionState.java
index ce2f3f924554..2670b03ca8ac 100644
--- a/telephony/java/android/telephony/PreciseDataConnectionState.java
+++ b/telephony/java/android/telephony/PreciseDataConnectionState.java
@@ -125,9 +125,9 @@ public final class PreciseDataConnectionState implements Parcelable {
mId = in.readInt();
mState = in.readInt();
mNetworkType = in.readInt();
- mLinkProperties = in.readParcelable(LinkProperties.class.getClassLoader());
+ mLinkProperties = in.readParcelable(LinkProperties.class.getClassLoader(), android.net.LinkProperties.class);
mFailCause = in.readInt();
- mApnSetting = in.readParcelable(ApnSetting.class.getClassLoader());
+ mApnSetting = in.readParcelable(ApnSetting.class.getClassLoader(), android.telephony.data.ApnSetting.class);
}
/**
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index 5affb62ae5cd..70da9b95410a 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -479,7 +479,7 @@ public class ServiceState implements Parcelable {
mIsEmergencyOnly = in.readInt() != 0;
mArfcnRsrpBoost = in.readInt();
synchronized (mNetworkRegistrationInfos) {
- in.readList(mNetworkRegistrationInfos, NetworkRegistrationInfo.class.getClassLoader());
+ in.readList(mNetworkRegistrationInfos, NetworkRegistrationInfo.class.getClassLoader(), android.telephony.NetworkRegistrationInfo.class);
}
mChannelNumber = in.readInt();
mCellBandwidths = in.createIntArray();
diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java
index b7bc46736e18..f74ef0fe764a 100644
--- a/telephony/java/android/telephony/SignalStrength.java
+++ b/telephony/java/android/telephony/SignalStrength.java
@@ -275,12 +275,12 @@ public class SignalStrength implements Parcelable {
public SignalStrength(Parcel in) {
if (DBG) log("Size of signalstrength parcel:" + in.dataSize());
- mCdma = in.readParcelable(CellSignalStrengthCdma.class.getClassLoader());
- mGsm = in.readParcelable(CellSignalStrengthGsm.class.getClassLoader());
- mWcdma = in.readParcelable(CellSignalStrengthWcdma.class.getClassLoader());
- mTdscdma = in.readParcelable(CellSignalStrengthTdscdma.class.getClassLoader());
- mLte = in.readParcelable(CellSignalStrengthLte.class.getClassLoader());
- mNr = in.readParcelable(CellSignalStrengthLte.class.getClassLoader());
+ mCdma = in.readParcelable(CellSignalStrengthCdma.class.getClassLoader(), android.telephony.CellSignalStrengthCdma.class);
+ mGsm = in.readParcelable(CellSignalStrengthGsm.class.getClassLoader(), android.telephony.CellSignalStrengthGsm.class);
+ mWcdma = in.readParcelable(CellSignalStrengthWcdma.class.getClassLoader(), android.telephony.CellSignalStrengthWcdma.class);
+ mTdscdma = in.readParcelable(CellSignalStrengthTdscdma.class.getClassLoader(), android.telephony.CellSignalStrengthTdscdma.class);
+ mLte = in.readParcelable(CellSignalStrengthLte.class.getClassLoader(), android.telephony.CellSignalStrengthLte.class);
+ mNr = in.readParcelable(CellSignalStrengthLte.class.getClassLoader(), android.telephony.CellSignalStrengthNr.class);
mTimestampMillis = in.readLong();
}
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 574a356c43f2..250e55cf5014 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -3892,6 +3892,11 @@ public class SubscriptionManager {
* {@link #PHONE_NUMBER_SOURCE_UICC UICC} and decide if the previously set phone number
* of source {@link #PHONE_NUMBER_SOURCE_CARRIER carrier} should be updated.
*
+ * <p>The API provides no guarantees of what format the number is in: the format can vary
+ * depending on the {@code source} and the network etc. Programmatic parsing should be done
+ * cautiously, for example, after formatting the number to a consistent format with
+ * {@link android.telephony.PhoneNumberUtils#formatNumberToE164(String, String)}.
+ *
* <p>Note the assumption is that one subscription (which usually means one SIM) has
* only one phone number. The multiple sources backup each other so hopefully at least one
* is availavle. For example, for a carrier that doesn't typically set phone numbers
@@ -3950,6 +3955,11 @@ public class SubscriptionManager {
* from available sources in the following order: {@link #PHONE_NUMBER_SOURCE_CARRIER}
* > {@link #PHONE_NUMBER_SOURCE_UICC} > {@link #PHONE_NUMBER_SOURCE_IMS}.
*
+ * <p>The API provides no guarantees of what format the number is in: the format can vary
+ * depending on the underlying source and the network etc. Programmatic parsing should be done
+ * cautiously, for example, after formatting the number to a consistent format with
+ * {@link android.telephony.PhoneNumberUtils#formatNumberToE164(String, String)}.
+ *
* @param subscriptionId the subscription ID, or {@link #DEFAULT_SUBSCRIPTION_ID}
* for the default one.
* @return the phone number, or an empty string if not available.
@@ -3988,6 +3998,9 @@ public class SubscriptionManager {
* <p>The API is suitable for carrier apps to provide a phone number, for example when
* it's not possible to update {@link #PHONE_NUMBER_SOURCE_UICC UICC} directly.
*
+ * <p>It's recommended that the phone number is formatted to well-known formats,
+ * for example, by {@link PhoneNumberUtils} {@code formatNumber*} methods.
+ *
* @param subscriptionId the subscription ID, or {@link #DEFAULT_SUBSCRIPTION_ID}
* for the default one.
* @param number the phone number, or an empty string to remove the previously set number.
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 0c56de82eafc..536517c12313 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -15138,6 +15138,15 @@ public class TelephonyManager {
public static final int CALL_WAITING_STATUS_NOT_SUPPORTED = 4;
/**
+ * Indicates the call waiting status could not be set or queried because the Fixed Dialing
+ * Numbers (FDN) feature is enabled.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int CALL_WAITING_STATUS_FDN_CHECK_FAILURE = 5;
+
+ /**
* @hide
*/
@IntDef(prefix = { "CALL_WAITING_STATUS_" }, value = {
@@ -15145,6 +15154,7 @@ public class TelephonyManager {
CALL_WAITING_STATUS_DISABLED,
CALL_WAITING_STATUS_UNKNOWN_ERROR,
CALL_WAITING_STATUS_NOT_SUPPORTED,
+ CALL_WAITING_STATUS_FDN_CHECK_FAILURE,
})
@Retention(RetentionPolicy.SOURCE)
public @interface CallWaitingStatus {
@@ -15165,6 +15175,7 @@ public class TelephonyManager {
* <li>{@link #CALL_WAITING_STATUS_DISABLED}}</li>
* <li>{@link #CALL_WAITING_STATUS_UNKNOWN_ERROR}}</li>
* <li>{@link #CALL_WAITING_STATUS_NOT_SUPPORTED}}</li>
+ * <li>{@link #CALL_WAITING_STATUS_FDN_CHECK_FAILURE}}</li>
* </ul>
* @hide
*/
@@ -15214,7 +15225,8 @@ public class TelephonyManager {
* {@link #CALL_WAITING_STATUS_ENABLED} or
* {@link #CALL_WAITING_STATUS_DISABLED} if the operation succeeded and
* {@link #CALL_WAITING_STATUS_NOT_SUPPORTED} or
- * {@link #CALL_WAITING_STATUS_UNKNOWN_ERROR} if it failed.
+ * {@link #CALL_WAITING_STATUS_UNKNOWN_ERROR} or
+ * {@link #CALL_WAITING_STATUS_FDN_CHECK_FAILURE} if it failed.
* @hide
*/
@SystemApi
diff --git a/telephony/java/android/telephony/ThermalMitigationRequest.java b/telephony/java/android/telephony/ThermalMitigationRequest.java
index 91ad9c3e1f51..a0676ea63711 100644
--- a/telephony/java/android/telephony/ThermalMitigationRequest.java
+++ b/telephony/java/android/telephony/ThermalMitigationRequest.java
@@ -100,7 +100,7 @@ public final class ThermalMitigationRequest implements Parcelable {
private ThermalMitigationRequest(Parcel in) {
mThermalMitigationAction = in.readInt();
- mDataThrottlingRequest = in.readParcelable(DataThrottlingRequest.class.getClassLoader());
+ mDataThrottlingRequest = in.readParcelable(DataThrottlingRequest.class.getClassLoader(), android.telephony.DataThrottlingRequest.class);
}
/**
diff --git a/telephony/java/android/telephony/VisualVoicemailSms.java b/telephony/java/android/telephony/VisualVoicemailSms.java
index 085f8823b840..bec715e3b81a 100644
--- a/telephony/java/android/telephony/VisualVoicemailSms.java
+++ b/telephony/java/android/telephony/VisualVoicemailSms.java
@@ -121,7 +121,7 @@ public final class VisualVoicemailSms implements Parcelable {
@Override
public VisualVoicemailSms createFromParcel(Parcel in) {
return new Builder()
- .setPhoneAccountHandle((PhoneAccountHandle) in.readParcelable(null))
+ .setPhoneAccountHandle((PhoneAccountHandle) in.readParcelable(null, android.telecom.PhoneAccountHandle.class))
.setPrefix(in.readString())
.setFields(in.readBundle())
.setMessageBody(in.readString())
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index cb112cf3b93a..acbd64b57773 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -1628,7 +1628,7 @@ public class ApnSetting implements Parcelable {
.setApnName(in.readString())
.setProxyAddress(in.readString())
.setProxyPort(in.readInt())
- .setMmsc(in.readParcelable(Uri.class.getClassLoader()))
+ .setMmsc(in.readParcelable(Uri.class.getClassLoader(), android.net.Uri.class))
.setMmsProxyAddress(in.readString())
.setMmsProxyPort(in.readInt())
.setUser(in.readString())
diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java
index ef02589abaf8..ae0d4e7e3b4e 100644
--- a/telephony/java/android/telephony/data/DataCallResponse.java
+++ b/telephony/java/android/telephony/data/DataCallResponse.java
@@ -241,24 +241,24 @@ public final class DataCallResponse implements Parcelable {
mProtocolType = source.readInt();
mInterfaceName = source.readString();
mAddresses = new ArrayList<>();
- source.readList(mAddresses, LinkAddress.class.getClassLoader());
+ source.readList(mAddresses, LinkAddress.class.getClassLoader(), android.net.LinkAddress.class);
mDnsAddresses = new ArrayList<>();
- source.readList(mDnsAddresses, InetAddress.class.getClassLoader());
+ source.readList(mDnsAddresses, InetAddress.class.getClassLoader(), java.net.InetAddress.class);
mGatewayAddresses = new ArrayList<>();
- source.readList(mGatewayAddresses, InetAddress.class.getClassLoader());
+ source.readList(mGatewayAddresses, InetAddress.class.getClassLoader(), java.net.InetAddress.class);
mPcscfAddresses = new ArrayList<>();
- source.readList(mPcscfAddresses, InetAddress.class.getClassLoader());
+ source.readList(mPcscfAddresses, InetAddress.class.getClassLoader(), java.net.InetAddress.class);
mMtu = source.readInt();
mMtuV4 = source.readInt();
mMtuV6 = source.readInt();
mHandoverFailureMode = source.readInt();
mPduSessionId = source.readInt();
- mDefaultQos = source.readParcelable(Qos.class.getClassLoader());
+ mDefaultQos = source.readParcelable(Qos.class.getClassLoader(), android.telephony.data.Qos.class);
mQosBearerSessions = new ArrayList<>();
- source.readList(mQosBearerSessions, QosBearerSession.class.getClassLoader());
- mSliceInfo = source.readParcelable(NetworkSliceInfo.class.getClassLoader());
+ source.readList(mQosBearerSessions, QosBearerSession.class.getClassLoader(), android.telephony.data.QosBearerSession.class);
+ mSliceInfo = source.readParcelable(NetworkSliceInfo.class.getClassLoader(), android.telephony.data.NetworkSliceInfo.class);
mTrafficDescriptors = new ArrayList<>();
- source.readList(mTrafficDescriptors, TrafficDescriptor.class.getClassLoader());
+ source.readList(mTrafficDescriptors, TrafficDescriptor.class.getClassLoader(), android.telephony.data.TrafficDescriptor.class);
}
/**
diff --git a/telephony/java/android/telephony/data/DataProfile.java b/telephony/java/android/telephony/data/DataProfile.java
index ec04c1ae9522..a166a5d6404c 100644
--- a/telephony/java/android/telephony/data/DataProfile.java
+++ b/telephony/java/android/telephony/data/DataProfile.java
@@ -107,8 +107,8 @@ public final class DataProfile implements Parcelable {
private DataProfile(Parcel source) {
mType = source.readInt();
- mApnSetting = source.readParcelable(ApnSetting.class.getClassLoader());
- mTrafficDescriptor = source.readParcelable(TrafficDescriptor.class.getClassLoader());
+ mApnSetting = source.readParcelable(ApnSetting.class.getClassLoader(), android.telephony.data.ApnSetting.class);
+ mTrafficDescriptor = source.readParcelable(TrafficDescriptor.class.getClassLoader(), android.telephony.data.TrafficDescriptor.class);
mPreferred = source.readBoolean();
mSetupTimestamp = source.readLong();
}
diff --git a/telephony/java/android/telephony/data/DataServiceCallback.java b/telephony/java/android/telephony/data/DataServiceCallback.java
index 051d6c5d5ec0..1ff6ec1779cd 100644
--- a/telephony/java/android/telephony/data/DataServiceCallback.java
+++ b/telephony/java/android/telephony/data/DataServiceCallback.java
@@ -253,8 +253,10 @@ public class DataServiceCallback {
return "RESULT_ERROR_BUSY";
case RESULT_ERROR_ILLEGAL_STATE:
return "RESULT_ERROR_ILLEGAL_STATE";
+ case RESULT_ERROR_TEMPORARILY_UNAVAILABLE:
+ return "RESULT_ERROR_TEMPORARILY_UNAVAILABLE";
default:
- return "Missing case for result code=" + resultCode;
+ return "Unknown(" + resultCode + ")";
}
}
diff --git a/telephony/java/android/telephony/data/Qos.java b/telephony/java/android/telephony/data/Qos.java
index 8c437c83e196..9c2a3bb1e15c 100644
--- a/telephony/java/android/telephony/data/Qos.java
+++ b/telephony/java/android/telephony/data/Qos.java
@@ -136,8 +136,8 @@ public abstract class Qos {
protected Qos(@NonNull Parcel source) {
type = source.readInt();
- downlink = source.readParcelable(QosBandwidth.class.getClassLoader());
- uplink = source.readParcelable(QosBandwidth.class.getClassLoader());
+ downlink = source.readParcelable(QosBandwidth.class.getClassLoader(), android.telephony.data.Qos.QosBandwidth.class);
+ uplink = source.readParcelable(QosBandwidth.class.getClassLoader(), android.telephony.data.Qos.QosBandwidth.class);
}
/**
diff --git a/telephony/java/android/telephony/data/QosBearerFilter.java b/telephony/java/android/telephony/data/QosBearerFilter.java
index d6f0cb02f0aa..0ab7b61bd73d 100644
--- a/telephony/java/android/telephony/data/QosBearerFilter.java
+++ b/telephony/java/android/telephony/data/QosBearerFilter.java
@@ -256,11 +256,11 @@ public final class QosBearerFilter implements Parcelable {
private QosBearerFilter(Parcel source) {
localAddresses = new ArrayList<>();
- source.readList(localAddresses, LinkAddress.class.getClassLoader());
+ source.readList(localAddresses, LinkAddress.class.getClassLoader(), android.net.LinkAddress.class);
remoteAddresses = new ArrayList<>();
- source.readList(remoteAddresses, LinkAddress.class.getClassLoader());
- localPort = source.readParcelable(PortRange.class.getClassLoader());
- remotePort = source.readParcelable(PortRange.class.getClassLoader());
+ source.readList(remoteAddresses, LinkAddress.class.getClassLoader(), android.net.LinkAddress.class);
+ localPort = source.readParcelable(PortRange.class.getClassLoader(), android.telephony.data.QosBearerFilter.PortRange.class);
+ remotePort = source.readParcelable(PortRange.class.getClassLoader(), android.telephony.data.QosBearerFilter.PortRange.class);
protocol = source.readInt();
typeOfServiceMask = source.readInt();
flowLabel = source.readLong();
diff --git a/telephony/java/android/telephony/data/QosBearerSession.java b/telephony/java/android/telephony/data/QosBearerSession.java
index ffeb08a17584..dd080856d450 100644
--- a/telephony/java/android/telephony/data/QosBearerSession.java
+++ b/telephony/java/android/telephony/data/QosBearerSession.java
@@ -46,9 +46,9 @@ public final class QosBearerSession implements Parcelable{
private QosBearerSession(Parcel source) {
qosBearerSessionId = source.readInt();
- qos = source.readParcelable(Qos.class.getClassLoader());
+ qos = source.readParcelable(Qos.class.getClassLoader(), android.telephony.data.Qos.class);
qosBearerFilterList = new ArrayList<>();
- source.readList(qosBearerFilterList, QosBearerFilter.class.getClassLoader());
+ source.readList(qosBearerFilterList, QosBearerFilter.class.getClassLoader(), android.telephony.data.QosBearerFilter.class);
}
public int getQosBearerSessionId() {
diff --git a/telephony/java/android/telephony/gba/GbaAuthRequest.java b/telephony/java/android/telephony/gba/GbaAuthRequest.java
index 5366e9af3147..2c6021a18ea2 100644
--- a/telephony/java/android/telephony/gba/GbaAuthRequest.java
+++ b/telephony/java/android/telephony/gba/GbaAuthRequest.java
@@ -120,7 +120,7 @@ public final class GbaAuthRequest implements Parcelable {
int token = in.readInt();
int subId = in.readInt();
int appType = in.readInt();
- Uri nafUrl = in.readParcelable(GbaAuthRequest.class.getClassLoader());
+ Uri nafUrl = in.readParcelable(GbaAuthRequest.class.getClassLoader(), android.net.Uri.class);
int len = in.readInt();
byte[] protocol = new byte[len];
in.readByteArray(protocol);
diff --git a/telephony/java/android/telephony/ims/DelegateRequest.java b/telephony/java/android/telephony/ims/DelegateRequest.java
index c322d924182a..c5c92009ee32 100644
--- a/telephony/java/android/telephony/ims/DelegateRequest.java
+++ b/telephony/java/android/telephony/ims/DelegateRequest.java
@@ -63,7 +63,7 @@ public final class DelegateRequest implements Parcelable {
*/
private DelegateRequest(Parcel in) {
mFeatureTags = new ArrayList<>();
- in.readList(mFeatureTags, null /*classLoader*/);
+ in.readList(mFeatureTags, null /*classLoader*/, java.lang.String.class);
}
@Override
diff --git a/telephony/java/android/telephony/ims/ImsCallProfile.java b/telephony/java/android/telephony/ims/ImsCallProfile.java
index 8a665dc92421..e6d7df34f755 100644
--- a/telephony/java/android/telephony/ims/ImsCallProfile.java
+++ b/telephony/java/android/telephony/ims/ImsCallProfile.java
@@ -843,7 +843,7 @@ public final class ImsCallProfile implements Parcelable {
mServiceType = in.readInt();
mCallType = in.readInt();
mCallExtras = in.readBundle();
- mMediaProfile = in.readParcelable(ImsStreamMediaProfile.class.getClassLoader());
+ mMediaProfile = in.readParcelable(ImsStreamMediaProfile.class.getClassLoader(), android.telephony.ims.ImsStreamMediaProfile.class);
mEmergencyServiceCategories = in.readInt();
mEmergencyUrns = in.createStringArrayList();
mEmergencyCallRouting = in.readInt();
diff --git a/telephony/java/android/telephony/ims/ImsCallSession.java b/telephony/java/android/telephony/ims/ImsCallSession.java
index 6569de626702..d65286f26447 100755
--- a/telephony/java/android/telephony/ims/ImsCallSession.java
+++ b/telephony/java/android/telephony/ims/ImsCallSession.java
@@ -1212,50 +1212,56 @@ public class ImsCallSession {
*/
@Override
public void callSessionInitiating(ImsCallProfile profile) {
- if (mListener != null) {
- TelephonyUtils.runWithCleanCallingIdentity(()-> mListener.callSessionInitiating(
- ImsCallSession.this, profile), mListenerExecutor);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionInitiating(ImsCallSession.this, profile);
+ }
+ }, mListenerExecutor);
}
@Override
public void callSessionProgressing(ImsStreamMediaProfile profile) {
- if (mListener != null) {
- TelephonyUtils.runWithCleanCallingIdentity(()-> mListener.callSessionProgressing(
- ImsCallSession.this, profile), mListenerExecutor);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionProgressing(ImsCallSession.this, profile);
+ }
+ }, mListenerExecutor);
}
@Override
public void callSessionInitiated(ImsCallProfile profile) {
- if (mListener != null) {
- TelephonyUtils.runWithCleanCallingIdentity(()-> mListener.callSessionStarted(
- ImsCallSession.this, profile), mListenerExecutor);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionStarted(ImsCallSession.this, profile);
+ }
+ }, mListenerExecutor);
}
@Override
public void callSessionInitiatingFailed(ImsReasonInfo reasonInfo) {
- if (mListener != null) {
- TelephonyUtils.runWithCleanCallingIdentity(()-> mListener.callSessionStartFailed(
- ImsCallSession.this, reasonInfo), mListenerExecutor);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionStartFailed(ImsCallSession.this, reasonInfo);
+ }
+ }, mListenerExecutor);
}
@Override
public void callSessionInitiatedFailed(ImsReasonInfo reasonInfo) {
- if (mListener != null) {
- TelephonyUtils.runWithCleanCallingIdentity(()-> mListener.callSessionStartFailed(
- ImsCallSession.this, reasonInfo), mListenerExecutor);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionStartFailed(ImsCallSession.this, reasonInfo);
+ }
+ }, mListenerExecutor);
}
@Override
public void callSessionTerminated(ImsReasonInfo reasonInfo) {
- if (mListener != null) {
- TelephonyUtils.runWithCleanCallingIdentity(()-> mListener.callSessionTerminated(
- ImsCallSession.this, reasonInfo), mListenerExecutor);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionTerminated(ImsCallSession.this, reasonInfo);
+ }
+ }, mListenerExecutor);
}
/**
@@ -1263,51 +1269,56 @@ public class ImsCallSession {
*/
@Override
public void callSessionHeld(ImsCallProfile profile) {
- if (mListener != null) {
- TelephonyUtils.runWithCleanCallingIdentity(()-> mListener.callSessionHeld(
- ImsCallSession.this, profile), mListenerExecutor);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionHeld(ImsCallSession.this, profile);
+ }
+ }, mListenerExecutor);
}
@Override
public void callSessionHoldFailed(ImsReasonInfo reasonInfo) {
- if (mListener != null) {
- TelephonyUtils.runWithCleanCallingIdentity(()-> mListener.callSessionHoldFailed(
- ImsCallSession.this, reasonInfo), mListenerExecutor);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionHoldFailed(ImsCallSession.this, reasonInfo);
+ }
+ }, mListenerExecutor);
}
@Override
public void callSessionHoldReceived(ImsCallProfile profile) {
- if (mListener != null) {
- TelephonyUtils.runWithCleanCallingIdentity(()-> mListener.callSessionHoldReceived(
- ImsCallSession.this, profile), mListenerExecutor);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionHoldReceived(ImsCallSession.this, profile);
+ }
+ }, mListenerExecutor);
}
@Override
public void callSessionResumed(ImsCallProfile profile) {
- if (mListener != null) {
- TelephonyUtils.runWithCleanCallingIdentity(()-> mListener.callSessionResumed(
- ImsCallSession.this, profile), mListenerExecutor);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionResumed(ImsCallSession.this, profile);
+ }
+ }, mListenerExecutor);
}
@Override
public void callSessionResumeFailed(ImsReasonInfo reasonInfo) {
- if (mListener != null) {
- TelephonyUtils.runWithCleanCallingIdentity(()-> mListener.callSessionResumeFailed(
- ImsCallSession.this, reasonInfo), mListenerExecutor);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionResumeFailed(ImsCallSession.this, reasonInfo);
+ }
+ }, mListenerExecutor);
}
@Override
public void callSessionResumeReceived(ImsCallProfile profile) {
- if (mListener != null) {
- TelephonyUtils.runWithCleanCallingIdentity(()->
- mListener.callSessionResumeReceived(ImsCallSession.this, profile),
- mListenerExecutor);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionResumeReceived(ImsCallSession.this, profile);
+ }
+ }, mListenerExecutor);
}
/**
@@ -1330,8 +1341,8 @@ public class ImsCallSession {
*/
@Override
public void callSessionMergeComplete(IImsCallSession newSession) {
- if (mListener != null) {
- TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
if (newSession != null) {
// New session created after conference
mListener.callSessionMergeComplete(new ImsCallSession(newSession));
@@ -1339,8 +1350,8 @@ public class ImsCallSession {
// Session already exists. Hence no need to pass
mListener.callSessionMergeComplete(null);
}
- }, mListenerExecutor);
- }
+ }
+ }, mListenerExecutor);
}
/**
@@ -1350,11 +1361,11 @@ public class ImsCallSession {
*/
@Override
public void callSessionMergeFailed(ImsReasonInfo reasonInfo) {
- if (mListener != null) {
- TelephonyUtils.runWithCleanCallingIdentity(()->
- mListener.callSessionMergeFailed(ImsCallSession.this, reasonInfo),
- mListenerExecutor);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionMergeFailed(ImsCallSession.this, reasonInfo);
+ }
+ }, mListenerExecutor);
}
/**
@@ -1362,29 +1373,29 @@ public class ImsCallSession {
*/
@Override
public void callSessionUpdated(ImsCallProfile profile) {
- if (mListener != null) {
- TelephonyUtils.runWithCleanCallingIdentity(()->
- mListener.callSessionUpdated(ImsCallSession.this, profile),
- mListenerExecutor);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionUpdated(ImsCallSession.this, profile);
+ }
+ }, mListenerExecutor);
}
@Override
public void callSessionUpdateFailed(ImsReasonInfo reasonInfo) {
- if (mListener != null) {
- TelephonyUtils.runWithCleanCallingIdentity(()->
- mListener.callSessionUpdateFailed(ImsCallSession.this, reasonInfo),
- mListenerExecutor);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionUpdateFailed(ImsCallSession.this, reasonInfo);
+ }
+ }, mListenerExecutor);
}
@Override
public void callSessionUpdateReceived(ImsCallProfile profile) {
- if (mListener != null) {
- TelephonyUtils.runWithCleanCallingIdentity(()->
- mListener.callSessionUpdateReceived(ImsCallSession.this, profile),
- mListenerExecutor);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionUpdateReceived(ImsCallSession.this, profile);
+ }
+ }, mListenerExecutor);
}
/**
@@ -1393,30 +1404,33 @@ public class ImsCallSession {
@Override
public void callSessionConferenceExtended(IImsCallSession newSession,
ImsCallProfile profile) {
- if (mListener != null) {
- TelephonyUtils.runWithCleanCallingIdentity(()->
- mListener.callSessionConferenceExtended(ImsCallSession.this,
- new ImsCallSession(newSession), profile), mListenerExecutor);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionConferenceExtended(ImsCallSession.this,
+ new ImsCallSession(newSession), profile);
+ }
+ }, mListenerExecutor);
}
@Override
public void callSessionConferenceExtendFailed(ImsReasonInfo reasonInfo) {
- if (mListener != null) {
- TelephonyUtils.runWithCleanCallingIdentity(()->
- mListener.callSessionConferenceExtendFailed(
- ImsCallSession.this, reasonInfo), mListenerExecutor);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionConferenceExtendFailed(
+ ImsCallSession.this, reasonInfo);
+ }
+ }, mListenerExecutor);
}
@Override
public void callSessionConferenceExtendReceived(IImsCallSession newSession,
ImsCallProfile profile) {
- if (mListener != null) {
- TelephonyUtils.runWithCleanCallingIdentity(()->
- mListener.callSessionConferenceExtendReceived(ImsCallSession.this,
- new ImsCallSession(newSession), profile), mListenerExecutor);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionConferenceExtendReceived(ImsCallSession.this,
+ new ImsCallSession(newSession), profile);
+ }
+ }, mListenerExecutor);
}
/**
@@ -1425,38 +1439,41 @@ public class ImsCallSession {
*/
@Override
public void callSessionInviteParticipantsRequestDelivered() {
- if (mListener != null) {
- TelephonyUtils.runWithCleanCallingIdentity(()->
- mListener.callSessionInviteParticipantsRequestDelivered(
- ImsCallSession.this), mListenerExecutor);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionInviteParticipantsRequestDelivered(
+ ImsCallSession.this);
+ }
+ }, mListenerExecutor);
}
@Override
public void callSessionInviteParticipantsRequestFailed(ImsReasonInfo reasonInfo) {
- if (mListener != null) {
- TelephonyUtils.runWithCleanCallingIdentity(()->
- mListener.callSessionInviteParticipantsRequestFailed(ImsCallSession.this,
- reasonInfo), mListenerExecutor);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionInviteParticipantsRequestFailed(ImsCallSession.this,
+ reasonInfo);
+ }
+ }, mListenerExecutor);
}
@Override
public void callSessionRemoveParticipantsRequestDelivered() {
- if (mListener != null) {
- TelephonyUtils.runWithCleanCallingIdentity(()->
- mListener.callSessionRemoveParticipantsRequestDelivered(
- ImsCallSession.this), mListenerExecutor);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionRemoveParticipantsRequestDelivered(ImsCallSession.this);
+ }
+ }, mListenerExecutor);
}
@Override
public void callSessionRemoveParticipantsRequestFailed(ImsReasonInfo reasonInfo) {
- if (mListener != null) {
- TelephonyUtils.runWithCleanCallingIdentity(()->
- mListener.callSessionRemoveParticipantsRequestFailed(ImsCallSession.this,
- reasonInfo), mListenerExecutor);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionRemoveParticipantsRequestFailed(ImsCallSession.this,
+ reasonInfo);
+ }
+ }, mListenerExecutor);
}
/**
@@ -1464,11 +1481,11 @@ public class ImsCallSession {
*/
@Override
public void callSessionConferenceStateUpdated(ImsConferenceState state) {
- if (mListener != null) {
- TelephonyUtils.runWithCleanCallingIdentity(()->
- mListener.callSessionConferenceStateUpdated(ImsCallSession.this, state),
- mListenerExecutor);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionConferenceStateUpdated(ImsCallSession.this, state);
+ }
+ }, mListenerExecutor);
}
/**
@@ -1476,11 +1493,12 @@ public class ImsCallSession {
*/
@Override
public void callSessionUssdMessageReceived(int mode, String ussdMessage) {
- if (mListener != null) {
- TelephonyUtils.runWithCleanCallingIdentity(()->
- mListener.callSessionUssdMessageReceived(ImsCallSession.this, mode,
- ussdMessage), mListenerExecutor);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionUssdMessageReceived(ImsCallSession.this, mode,
+ ussdMessage);
+ }
+ }, mListenerExecutor);
}
/**
@@ -1496,11 +1514,12 @@ public class ImsCallSession {
*/
@Override
public void callSessionMayHandover(int srcNetworkType, int targetNetworkType) {
- if (mListener != null) {
- TelephonyUtils.runWithCleanCallingIdentity(()->
- mListener.callSessionMayHandover(ImsCallSession.this, srcNetworkType,
- targetNetworkType), mListenerExecutor);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionMayHandover(ImsCallSession.this, srcNetworkType,
+ targetNetworkType);
+ }
+ }, mListenerExecutor);
}
/**
@@ -1509,11 +1528,12 @@ public class ImsCallSession {
@Override
public void callSessionHandover(int srcNetworkType, int targetNetworkType,
ImsReasonInfo reasonInfo) {
- if (mListener != null) {
- TelephonyUtils.runWithCleanCallingIdentity(()->
- mListener.callSessionHandover(ImsCallSession.this, srcNetworkType,
- targetNetworkType, reasonInfo), mListenerExecutor);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionHandover(ImsCallSession.this, srcNetworkType,
+ targetNetworkType, reasonInfo);
+ }
+ }, mListenerExecutor);
}
/**
@@ -1522,11 +1542,12 @@ public class ImsCallSession {
@Override
public void callSessionHandoverFailed(int srcNetworkType, int targetNetworkType,
ImsReasonInfo reasonInfo) {
- if (mListener != null) {
- TelephonyUtils.runWithCleanCallingIdentity(()->
- mListener.callSessionHandoverFailed(ImsCallSession.this, srcNetworkType,
- targetNetworkType, reasonInfo), mListenerExecutor);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionHandoverFailed(ImsCallSession.this, srcNetworkType,
+ targetNetworkType, reasonInfo);
+ }
+ }, mListenerExecutor);
}
/**
@@ -1534,11 +1555,11 @@ public class ImsCallSession {
*/
@Override
public void callSessionTtyModeReceived(int mode) {
- if (mListener != null) {
- TelephonyUtils.runWithCleanCallingIdentity(()->
- mListener.callSessionTtyModeReceived(ImsCallSession.this, mode),
- mListenerExecutor);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionTtyModeReceived(ImsCallSession.this, mode);
+ }
+ }, mListenerExecutor);
}
/**
@@ -1548,20 +1569,22 @@ public class ImsCallSession {
* otherwise.
*/
public void callSessionMultipartyStateChanged(boolean isMultiParty) {
- if (mListener != null) {
- TelephonyUtils.runWithCleanCallingIdentity(()->
- mListener.callSessionMultipartyStateChanged(ImsCallSession.this,
- isMultiParty), mListenerExecutor);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionMultipartyStateChanged(ImsCallSession.this,
+ isMultiParty);
+ }
+ }, mListenerExecutor);
}
@Override
public void callSessionSuppServiceReceived(ImsSuppServiceNotification suppServiceInfo ) {
- if (mListener != null) {
- TelephonyUtils.runWithCleanCallingIdentity(()->
- mListener.callSessionSuppServiceReceived(ImsCallSession.this,
- suppServiceInfo), mListenerExecutor);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionSuppServiceReceived(ImsCallSession.this,
+ suppServiceInfo);
+ }
+ }, mListenerExecutor);
}
/**
@@ -1569,11 +1592,12 @@ public class ImsCallSession {
*/
@Override
public void callSessionRttModifyRequestReceived(ImsCallProfile callProfile) {
- if (mListener != null) {
- TelephonyUtils.runWithCleanCallingIdentity(()->
- mListener.callSessionRttModifyRequestReceived(ImsCallSession.this,
- callProfile), mListenerExecutor);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionRttModifyRequestReceived(ImsCallSession.this,
+ callProfile);
+ }
+ }, mListenerExecutor);
}
/**
@@ -1581,11 +1605,11 @@ public class ImsCallSession {
*/
@Override
public void callSessionRttModifyResponseReceived(int status) {
- if (mListener != null) {
- TelephonyUtils.runWithCleanCallingIdentity(()->
- mListener.callSessionRttModifyResponseReceived(status),
- mListenerExecutor);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionRttModifyResponseReceived(status);
+ }
+ }, mListenerExecutor);
}
/**
@@ -1593,10 +1617,11 @@ public class ImsCallSession {
*/
@Override
public void callSessionRttMessageReceived(String rttMessage) {
- if (mListener != null) {
- TelephonyUtils.runWithCleanCallingIdentity(()->
- mListener.callSessionRttMessageReceived(rttMessage), mListenerExecutor);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionRttMessageReceived(rttMessage);
+ }
+ }, mListenerExecutor);
}
/**
@@ -1604,28 +1629,29 @@ public class ImsCallSession {
*/
@Override
public void callSessionRttAudioIndicatorChanged(ImsStreamMediaProfile profile) {
- if (mListener != null) {
- TelephonyUtils.runWithCleanCallingIdentity(()->
- mListener.callSessionRttAudioIndicatorChanged(profile),
- mListenerExecutor);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionRttAudioIndicatorChanged(profile);
+ }
+ }, mListenerExecutor);
}
@Override
public void callSessionTransferred() {
- if (mListener != null) {
- TelephonyUtils.runWithCleanCallingIdentity(()->
- mListener.callSessionTransferred(ImsCallSession.this), mListenerExecutor);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionTransferred(ImsCallSession.this);
+ }
+ }, mListenerExecutor);
}
@Override
public void callSessionTransferFailed(@Nullable ImsReasonInfo reasonInfo) {
- if (mListener != null) {
- TelephonyUtils.runWithCleanCallingIdentity(()->
- mListener.callSessionTransferFailed(ImsCallSession.this, reasonInfo),
- mListenerExecutor);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionTransferFailed(ImsCallSession.this, reasonInfo);
+ }
+ }, mListenerExecutor);
}
/**
@@ -1634,10 +1660,11 @@ public class ImsCallSession {
*/
@Override
public void callSessionDtmfReceived(char dtmf) {
- if (mListener != null) {
- TelephonyUtils.runWithCleanCallingIdentity(()-> mListener.callSessionDtmfReceived(
- dtmf), mListenerExecutor);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionDtmfReceived(dtmf);
+ }
+ }, mListenerExecutor);
}
/**
@@ -1645,10 +1672,11 @@ public class ImsCallSession {
*/
@Override
public void callQualityChanged(CallQuality callQuality) {
- if (mListener != null) {
- TelephonyUtils.runWithCleanCallingIdentity(()-> mListener.callQualityChanged(
- callQuality), mListenerExecutor);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callQualityChanged(callQuality);
+ }
+ }, mListenerExecutor);
}
/**
@@ -1658,11 +1686,12 @@ public class ImsCallSession {
@Override
public void callSessionRtpHeaderExtensionsReceived(
@NonNull List<RtpHeaderExtension> extensions) {
- if (mListener != null) {
- TelephonyUtils.runWithCleanCallingIdentity(()->
- mListener.callSessionRtpHeaderExtensionsReceived(
- new ArraySet<RtpHeaderExtension>(extensions)), mListenerExecutor);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (mListener != null) {
+ mListener.callSessionRtpHeaderExtensionsReceived(
+ new ArraySet<RtpHeaderExtension>(extensions));
+ }
+ }, mListenerExecutor);
}
}
diff --git a/telephony/java/android/telephony/ims/ImsConferenceState.java b/telephony/java/android/telephony/ims/ImsConferenceState.java
index 1fa5f52968e5..d4d8c44196d5 100644
--- a/telephony/java/android/telephony/ims/ImsConferenceState.java
+++ b/telephony/java/android/telephony/ims/ImsConferenceState.java
@@ -133,7 +133,7 @@ public final class ImsConferenceState implements Parcelable {
for (int i = 0; i < size; ++i) {
String user = in.readString();
- Bundle state = in.readParcelable(null);
+ Bundle state = in.readParcelable(null, android.os.Bundle.class);
mParticipants.put(user, state);
}
}
diff --git a/telephony/java/android/telephony/ims/ImsExternalCallState.java b/telephony/java/android/telephony/ims/ImsExternalCallState.java
index c663e393fe06..d45110772ce4 100644
--- a/telephony/java/android/telephony/ims/ImsExternalCallState.java
+++ b/telephony/java/android/telephony/ims/ImsExternalCallState.java
@@ -141,8 +141,8 @@ public final class ImsExternalCallState implements Parcelable {
public ImsExternalCallState(Parcel in) {
mCallId = in.readInt();
ClassLoader classLoader = ImsExternalCallState.class.getClassLoader();
- mAddress = in.readParcelable(classLoader);
- mLocalAddress = in.readParcelable(classLoader);
+ mAddress = in.readParcelable(classLoader, android.net.Uri.class);
+ mLocalAddress = in.readParcelable(classLoader, android.net.Uri.class);
mIsPullable = (in.readInt() != 0);
mCallState = in.readInt();
mCallType = in.readInt();
diff --git a/telephony/java/android/telephony/ims/ImsRegistrationAttributes.java b/telephony/java/android/telephony/ims/ImsRegistrationAttributes.java
index ccb3231526dd..b77d3063e2cc 100644
--- a/telephony/java/android/telephony/ims/ImsRegistrationAttributes.java
+++ b/telephony/java/android/telephony/ims/ImsRegistrationAttributes.java
@@ -153,7 +153,7 @@ public final class ImsRegistrationAttributes implements Parcelable {
mTransportType = source.readInt();
mImsAttributeFlags = source.readInt();
mFeatureTags = new ArrayList<>();
- source.readList(mFeatureTags, null /*classloader*/);
+ source.readList(mFeatureTags, null /*classloader*/, java.lang.String.class);
}
/**
diff --git a/telephony/java/android/telephony/ims/ImsSsData.java b/telephony/java/android/telephony/ims/ImsSsData.java
index 868dea6a3121..9f4b77e22dd7 100644
--- a/telephony/java/android/telephony/ims/ImsSsData.java
+++ b/telephony/java/android/telephony/ims/ImsSsData.java
@@ -365,8 +365,8 @@ public final class ImsSsData implements Parcelable {
serviceClass = in.readInt();
result = in.readInt();
mSsInfo = in.createIntArray();
- mCfInfo = in.readParcelableList(new ArrayList<>(), this.getClass().getClassLoader());
- mImsSsInfo = in.readParcelableList(new ArrayList<>(), this.getClass().getClassLoader());
+ mCfInfo = in.readParcelableList(new ArrayList<>(), this.getClass().getClassLoader(), android.telephony.ims.ImsCallForwardInfo.class);
+ mImsSsInfo = in.readParcelableList(new ArrayList<>(), this.getClass().getClassLoader(), android.telephony.ims.ImsSsInfo.class);
}
public static final @android.annotation.NonNull Creator<ImsSsData> CREATOR = new Creator<ImsSsData>() {
diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java
index dbf4c99939de..677c1a9a7d76 100644
--- a/telephony/java/android/telephony/ims/ProvisioningManager.java
+++ b/telephony/java/android/telephony/ims/ProvisioningManager.java
@@ -34,6 +34,7 @@ import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyFrameworkInitializer;
import android.telephony.TelephonyManager;
+import android.telephony.ims.aidl.IFeatureProvisioningCallback;
import android.telephony.ims.aidl.IImsConfigCallback;
import android.telephony.ims.aidl.IRcsConfigCallback;
import android.telephony.ims.feature.MmTelFeature;
@@ -54,18 +55,12 @@ import java.util.concurrent.Executor;
* IMS provisioning keys are defined per carrier or OEM using OMA-DM or other provisioning
* applications and may vary. It is up to the carrier and OEM applications to ensure that the
* correct provisioning keys are being used when integrating with a vendor's ImsService.
- *
- * Note: For compatibility purposes, the integer values [0 - 99] used in
- * {@link #setProvisioningIntValue(int, int)} have been reserved for existing provisioning keys
- * previously defined in the Android framework. Please do not redefine new provisioning keys in this
- * range or it may generate collisions with existing keys. Some common constants have also been
- * defined in this class to make integrating with other system apps easier.
- * @hide
*/
-@SystemApi
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_IMS)
public class ProvisioningManager {
+ private static final String TAG = "ProvisioningManager";
+
/**@hide*/
@StringDef(prefix = "STRING_QUERY_RESULT_ERROR_", value = {
STRING_QUERY_RESULT_ERROR_GENERIC,
@@ -76,14 +71,18 @@ public class ProvisioningManager {
/**
* The query from {@link #getProvisioningStringValue(int)} has resulted in an unspecified error.
+ * @hide
*/
+ @SystemApi
public static final String STRING_QUERY_RESULT_ERROR_GENERIC =
"STRING_QUERY_RESULT_ERROR_GENERIC";
/**
* The query from {@link #getProvisioningStringValue(int)} has resulted in an error because the
* ImsService implementation was not ready for provisioning queries.
+ * @hide
*/
+ @SystemApi
public static final String STRING_QUERY_RESULT_ERROR_NOT_READY =
"STRING_QUERY_RESULT_ERROR_NOT_READY";
@@ -95,12 +94,16 @@ public class ProvisioningManager {
/**
* The integer result of provisioning for the queried key is disabled.
+ * @hide
*/
+ @SystemApi
public static final int PROVISIONING_VALUE_DISABLED = 0;
/**
* The integer result of provisioning for the queried key is enabled.
+ * @hide
*/
+ @SystemApi
public static final int PROVISIONING_VALUE_ENABLED = 1;
@@ -445,27 +448,31 @@ public class ProvisioningManager {
/**
* Override the user-defined WiFi Roaming enabled setting for this subscription, defined in
- * {@link SubscriptionManager#WFC_ROAMING_ENABLED_CONTENT_URI}, for the purposes of provisioning
- * the subscription for WiFi Calling.
+ * {@link android.telephony.SubscriptionManager#WFC_ROAMING_ENABLED_CONTENT_URI},
+ * for the purposes of provisioning the subscription for WiFi Calling.
*
- * @see #getProvisioningIntValue(int)
* @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ * @hide
*/
+ @SystemApi
public static final int KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE = 26;
/**
* Override the user-defined WiFi mode for this subscription, defined in
- * {@link SubscriptionManager#WFC_MODE_CONTENT_URI}, for the purposes of provisioning
- * this subscription for WiFi Calling.
+ * {@link android.telephony.SubscriptionManager#WFC_MODE_CONTENT_URI},
+ * for the purposes of provisioning this subscription for WiFi Calling.
*
* Valid values for this key are:
* {@link ImsMmTelManager#WIFI_MODE_WIFI_ONLY},
* {@link ImsMmTelManager#WIFI_MODE_CELLULAR_PREFERRED}, or
* {@link ImsMmTelManager#WIFI_MODE_WIFI_PREFERRED}.
*
- * @see #getProvisioningIntValue(int)
* @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ * @hide
*/
+ @SystemApi
public static final int KEY_VOICE_OVER_WIFI_MODE_OVERRIDE = 27;
/**
@@ -864,7 +871,9 @@ public class ProvisioningManager {
* <p>Value is in String format.
* @see #setProvisioningStringValue(int, String)
* @see #getProvisioningStringValue(int)
+ * @hide
*/
+ @SystemApi
public static final int KEY_VOICE_OVER_WIFI_ENTITLEMENT_ID = 67;
/**
@@ -886,7 +895,9 @@ public class ProvisioningManager {
/**
* Callback for IMS provisioning changes.
+ * @hide
*/
+ @SystemApi
public static class Callback {
private static class CallbackBinder extends IImsConfigCallback.Stub {
@@ -956,11 +967,105 @@ public class ProvisioningManager {
}
}
+ /**
+ * Callback for IMS provisioning feature changes.
+ */
+ public static class FeatureProvisioningCallback {
+
+ private static class CallbackBinder extends IFeatureProvisioningCallback.Stub {
+
+ private final FeatureProvisioningCallback mFeatureProvisioningCallback;
+ private Executor mExecutor;
+
+ private CallbackBinder(FeatureProvisioningCallback featureProvisioningCallback) {
+ mFeatureProvisioningCallback = featureProvisioningCallback;
+ }
+
+ @Override
+ public final void onFeatureProvisioningChanged(
+ int capability, int tech, boolean isProvisioned) {
+ final long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() ->
+ mFeatureProvisioningCallback.onFeatureProvisioningChanged(
+ capability, tech, isProvisioned));
+ } finally {
+ restoreCallingIdentity(callingIdentity);
+ }
+ }
+
+ @Override
+ public final void onRcsFeatureProvisioningChanged(
+ int capability, int tech, boolean isProvisioned) {
+ final long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() ->
+ mFeatureProvisioningCallback.onRcsFeatureProvisioningChanged(
+ capability, tech, isProvisioned));
+ } finally {
+ restoreCallingIdentity(callingIdentity);
+ }
+ }
+
+ private void setExecutor(Executor executor) {
+ mExecutor = executor;
+ }
+ }
+
+ private final CallbackBinder mBinder = new CallbackBinder(this);
+
+ /**
+ * The IMS MMTEL provisioning has changed for a specific capability and IMS
+ * registration technology.
+ * @param capability The MMTEL capability that provisioning has changed for.
+ * @param tech The IMS registration technology associated with the MMTEL capability that
+ * provisioning has changed for.
+ * @param isProvisioned {@code true} if the capability is provisioned for the technology
+ * specified, or {@code false} if the capability is not provisioned for the technology
+ * specified.
+ */
+ public void onFeatureProvisioningChanged(
+ @MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
+ @ImsRegistrationImplBase.ImsRegistrationTech int tech,
+ boolean isProvisioned) {
+ // Base Implementation
+ }
+
+ /**
+ * The IMS RCS provisioning has changed for a specific capability and IMS
+ * registration technology.
+ * @param capability The RCS capability that provisioning has changed for.
+ * @param tech The IMS registration technology associated with the RCS capability that
+ * provisioning has changed for.
+ * @param isProvisioned {@code true} if the capability is provisioned for the technology
+ * specified, or {@code false} if the capability is not provisioned for the technology
+ * specified.
+ */
+ public void onRcsFeatureProvisioningChanged(
+ @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability,
+ @ImsRegistrationImplBase.ImsRegistrationTech int tech,
+ boolean isProvisioned) {
+ // Base Implementation
+ }
+
+ /**@hide*/
+ public final IFeatureProvisioningCallback getBinder() {
+ return mBinder;
+ }
+
+ /**@hide*/
+ public void setExecutor(Executor executor) {
+ mBinder.setExecutor(executor);
+ }
+ }
+
private int mSubId;
/**
* The callback for RCS provisioning changes.
+ * @hide
*/
+ @SystemApi
public static class RcsProvisioningCallback {
private static class CallbackBinder extends IRcsConfigCallback.Stub {
@@ -1098,7 +1203,9 @@ public class ProvisioningManager {
* @param subId The ID of the subscription that this ProvisioningManager will use.
* @see android.telephony.SubscriptionManager#getActiveSubscriptionInfoList()
* @throws IllegalArgumentException if the subscription is invalid.
+ * @hide
*/
+ @SystemApi
public static @NonNull ProvisioningManager createForSubscriptionId(int subId) {
if (!SubscriptionManager.isValidSubscriptionId(subId)) {
throw new IllegalArgumentException("Invalid subscription ID");
@@ -1107,7 +1214,9 @@ public class ProvisioningManager {
return new ProvisioningManager(subId);
}
- private ProvisioningManager(int subId) {
+ /**@hide*/
+ //@SystemApi
+ public ProvisioningManager(int subId) {
mSubId = subId;
}
@@ -1116,6 +1225,12 @@ public class ProvisioningManager {
*
* When the subscription associated with this callback is removed (SIM removed, ESIM swap,
* etc...), this callback will automatically be removed.
+ *
+ * <p> Requires Permission:
+ * <ul>
+ * <li>{@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE},</li>
+ * </ul>
+ *
* @param executor The {@link Executor} to call the callback methods on
* @param callback The provisioning callbackto be registered.
* @see #unregisterProvisioningChangedCallback(Callback)
@@ -1126,7 +1241,9 @@ public class ProvisioningManager {
* the {@link ImsService} associated with the subscription is not available. This can happen if
* the service crashed, for example. See {@link ImsException#getCode()} for a more detailed
* reason.
+ * @hide
*/
+ @SystemApi
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public void registerProvisioningChangedCallback(@NonNull @CallbackExecutor Executor executor,
@NonNull Callback callback) throws ImsException {
@@ -1144,12 +1261,20 @@ public class ProvisioningManager {
* Unregister an existing {@link Callback}. When the subscription associated with this
* callback is removed (SIM removed, ESIM swap, etc...), this callback will automatically be
* removed. If this method is called for an inactive subscription, it will result in a no-op.
+ *
+ * <p> Requires Permission:
+ * <ul>
+ * <li>{@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE},</li>
+ * </ul>
+ *
* @param callback The existing {@link Callback} to be removed.
* @see #registerProvisioningChangedCallback(Executor, Callback)
*
* @throws IllegalArgumentException if the subscription associated with this callback is
* invalid.
+ * @hide
*/
+ @SystemApi
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public void unregisterProvisioningChangedCallback(@NonNull Callback callback) {
try {
@@ -1160,6 +1285,62 @@ public class ProvisioningManager {
}
/**
+ * Register a new {@link FeatureProvisioningCallback}, which is used to listen for
+ * IMS feature provisioning updates.
+ * <p>
+ * When the subscription associated with this callback is removed (SIM removed,
+ * ESIM swap,etc...), this callback will automatically be removed.
+ *
+ * <p> Requires Permission:
+ * <ul>
+ * <li> android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE,</li>
+ * <li>{@link android.Manifest.permission#READ_PRECISE_PHONE_STATE},</li>
+ * <li>or that the caller has carrier privileges (see
+ * {@link TelephonyManager#hasCarrierPrivileges()}).</li>
+ * </ul>
+ *
+ * @param executor The executor that the callback methods will be called on.
+ * @param callback The callback instance being registered.
+ * @throws ImsException if the subscription associated with this callback is
+ * valid, but the {@link ImsService the service crashed, for example. See
+ * {@link ImsException#getCode()} for a more detailed reason.
+ */
+ @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
+ public void registerFeatureProvisioningChangedCallback(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull FeatureProvisioningCallback callback) throws ImsException {
+ callback.setExecutor(executor);
+ try {
+ getITelephony().registerFeatureProvisioningChangedCallback(mSubId,
+ callback.getBinder());
+ } catch (ServiceSpecificException e) {
+ throw new ImsException(e.getMessage(), e.errorCode);
+ } catch (RemoteException | IllegalStateException e) {
+ throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+ }
+
+ /**
+ * Unregisters a previously registered {@link FeatureProvisioningCallback}
+ * instance. When the subscription associated with this
+ * callback is removed (SIM removed, ESIM swap, etc...), this callback will
+ * automatically be removed. If this method is called for an inactive
+ * subscription, it will result in a no-op.
+ *
+ * @param callback The existing {@link FeatureProvisioningCallback} to be removed.
+ * @see #registerFeatureProvisioningChangedCallback(Executor, FeatureProvisioningCallback)
+ */
+ public void unregisterFeatureProvisioningChangedCallback(
+ @NonNull FeatureProvisioningCallback callback) {
+ try {
+ getITelephony().unregisterFeatureProvisioningChangedCallback(mSubId,
+ callback.getBinder());
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
* Query for the integer value associated with the provided key.
*
* This operation is blocking and should not be performed on the UI thread.
@@ -1168,7 +1349,9 @@ public class ProvisioningManager {
* @return an integer value for the provided key, or
* {@link ImsConfigImplBase#CONFIG_RESULT_UNKNOWN} if the key doesn't exist.
* @throws IllegalArgumentException if the key provided was invalid.
+ * @hide
*/
+ @SystemApi
@WorkerThread
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public int getProvisioningIntValue(int key) {
@@ -1188,7 +1371,9 @@ public class ProvisioningManager {
* @return a String value for the provided key, {@code null} if the key doesn't exist, or
* {@link StringResultError} if there was an error getting the value for the provided key.
* @throws IllegalArgumentException if the key provided was invalid.
+ * @hide
*/
+ @SystemApi
@WorkerThread
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public @Nullable @StringResultError String getProvisioningStringValue(int key) {
@@ -1209,7 +1394,15 @@ public class ProvisioningManager {
* @param key An integer that represents the provisioning key, which is defined by the OEM.
* @param value a integer value for the provided key.
* @return the result of setting the configuration value.
+ * @hide
+ *
+ * Note: For compatibility purposes, the integer values [0 - 99] used in
+ * {@link #setProvisioningIntValue(int, int)} have been reserved for existing provisioning keys
+ * previously defined in the Android framework. Please do not redefine new provisioning keys
+ * in this range or it may generate collisions with existing keys. Some common constants have
+ * also been defined in this class to make integrating with other system apps easier.
*/
+ @SystemApi
@WorkerThread
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
public @ImsConfigImplBase.SetConfigResult int setProvisioningIntValue(int key, int value) {
@@ -1229,7 +1422,9 @@ public class ProvisioningManager {
* should be appropriately namespaced to avoid collision.
* @param value a String value for the provided key.
* @return the result of setting the configuration value.
+ * @hide
*/
+ @SystemApi
@WorkerThread
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
public @ImsConfigImplBase.SetConfigResult int setProvisioningStringValue(int key,
@@ -1249,8 +1444,14 @@ public class ProvisioningManager {
* does not support the capability/technology combination specified, this operation will be a
* no-op.
*
- * @see CarrierConfigManager#KEY_CARRIER_UT_PROVISIONING_REQUIRED_BOOL
- * @see CarrierConfigManager#KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL
+ * <p>Requires Permission:
+ * <ul>
+ * <li>{@link android.Manifest.permission#MODIFY_PHONE_STATE},</li>
+ * <li>or that the calling app has carrier privileges (see</li>
+ * <li>{@link TelephonyManager#hasCarrierPrivileges}).</li>
+ * </ul>
+ *
+ * @see CarrierConfigManager.Ims#KEY_MMTEL_REQUIRES_PROVISIONING_BUNDLE
* @param isProvisioned true if the device is provisioned for UT over IMS, false otherwise.
*/
@WorkerThread
@@ -1258,9 +1459,10 @@ public class ProvisioningManager {
public void setProvisioningStatusForCapability(
@MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
@ImsRegistrationImplBase.ImsRegistrationTech int tech, boolean isProvisioned) {
+
try {
getITelephony().setImsProvisioningStatusForCapability(mSubId, capability, tech,
- isProvisioned);
+ isProvisioned);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
@@ -1274,14 +1476,21 @@ public class ProvisioningManager {
* {@link ImsRegistrationImplBase.ImsRegistrationTech} combination specified, this method will
* always return {@code true}.
*
- * @see CarrierConfigManager#KEY_CARRIER_UT_PROVISIONING_REQUIRED_BOOL
- * @see CarrierConfigManager#KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL
+ * <p> Requires Permission:
+ * <ul>
+ * <li>android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE,</li>
+ * <li>{@link android.Manifest.permission#READ_PRECISE_PHONE_STATE},</li>
+ * <li>or that the caller has carrier privileges (see
+ * {@link TelephonyManager#hasCarrierPrivileges()}).</li>
+ * </ul>
+ *
+ * @see CarrierConfigManager.Ims#KEY_MMTEL_REQUIRES_PROVISIONING_BUNDLE
* @return true if the device is provisioned for the capability or does not require
* provisioning, false if the capability does require provisioning and has not been
* provisioned yet.
*/
@WorkerThread
- @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
public boolean getProvisioningStatusForCapability(
@MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
@ImsRegistrationImplBase.ImsRegistrationTech int tech) {
@@ -1299,17 +1508,55 @@ public class ProvisioningManager {
* {@link RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag} this method will always return
* {@code true}.
*
- * @see CarrierConfigManager#KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL
+ * @see CarrierConfigManager.Ims#KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL
* @return true if the device is provisioned for the capability or does not require
* provisioning, false if the capability does require provisioning and has not been
* provisioned yet.
+ * @deprecated Use {@link #getRcsProvisioningStatusForCapability(int, int)} instead,
+ * as this only retrieves provisioning information for
+ * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE}
+ * @hide
*/
+ @Deprecated
+ @SystemApi
@WorkerThread
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public boolean getRcsProvisioningStatusForCapability(
@RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability) {
try {
- return getITelephony().getRcsProvisioningStatusForCapability(mSubId, capability);
+ return getITelephony().getRcsProvisioningStatusForCapability(mSubId, capability,
+ ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Get the provisioning status for the IMS RCS capability specified.
+ *
+ * If provisioning is not required for the queried
+ * {@link RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag} this method
+ * will always return {@code true}.
+ *
+ * <p> Requires Permission:
+ * <ul>
+ * <li>{@link android.Manifest.permission#READ_PRECISE_PHONE_STATE},</li>
+ * <li>or that the caller has carrier privileges (see
+ * {@link TelephonyManager#hasCarrierPrivileges()}).</li>
+ * </ul>
+ *
+ * @see CarrierConfigManager.Ims#KEY_RCS_REQUIRES_PROVISIONING_BUNDLE
+ * @return true if the device is provisioned for the capability or does not require
+ * provisioning, false if the capability does require provisioning and has not been
+ * provisioned yet.
+ */
+ @WorkerThread
+ @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
+ public boolean getRcsProvisioningStatusForCapability(
+ @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability,
+ @ImsRegistrationImplBase.ImsRegistrationTech int tech) {
+ try {
+ return getITelephony().getRcsProvisioningStatusForCapability(mSubId, capability, tech);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
@@ -1318,6 +1565,13 @@ public class ProvisioningManager {
/**
* Set the provisioning status for the IMS RCS capability using the specified subscription.
*
+ * <p> Requires Permission:
+ * <ul>
+ * <li>{@link android.Manifest.permission#MODIFY_PHONE_STATE}</li>
+ * <li>or that the caller has carrier privileges (see
+ * {@link TelephonyManager#hasCarrierPrivileges()}).</li>
+ * </ul>
+
* Provisioning may or may not be required, depending on the carrier configuration. If
* provisioning is not required for the carrier associated with this subscription or the device
* does not support the capability/technology combination specified, this operation will be a
@@ -1326,7 +1580,13 @@ public class ProvisioningManager {
* @see CarrierConfigManager#KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL
* @param isProvisioned true if the device is provisioned for the RCS capability specified,
* false otherwise.
+ * @deprecated Use {@link #setRcsProvisioningStatusForCapability(int, int, boolean)} instead,
+ * as this method only sets provisioning information for
+ * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE}
+ * @hide
*/
+ @Deprecated
+ @SystemApi
@WorkerThread
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
public void setRcsProvisioningStatusForCapability(
@@ -1334,28 +1594,121 @@ public class ProvisioningManager {
boolean isProvisioned) {
try {
getITelephony().setRcsProvisioningStatusForCapability(mSubId, capability,
- isProvisioned);
+ ImsRegistrationImplBase.REGISTRATION_TECH_LTE, isProvisioned);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
}
/**
+ * Set the provisioning status for the IMS RCS capability using the specified subscription.
+ *
+ * Provisioning may or may not be required, depending on the carrier configuration. If
+ * provisioning is not required for the carrier associated with this subscription or the device
+ * does not support the capability/technology combination specified, this operation will be a
+ * no-op.
+ *
+ * <p> Requires Permission:
+ * <ul>
+ * <li>{@link android.Manifest.permission#MODIFY_PHONE_STATE},</li>
+ * <li>or that the caller has carrier privileges (see
+ * {@link TelephonyManager#hasCarrierPrivileges()}).</li>
+ * </ul>
+ *
+ * @see CarrierConfigManager.Ims#KEY_RCS_REQUIRES_PROVISIONING_BUNDLE
+ * @param isProvisioned true if the device is provisioned for the RCS capability specified,
+ * false otherwise.
+ */
+ @WorkerThread
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public void setRcsProvisioningStatusForCapability(
+ @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability,
+ @ImsRegistrationImplBase.ImsRegistrationTech int tech, boolean isProvisioned) {
+ try {
+ getITelephony().setRcsProvisioningStatusForCapability(mSubId, capability,
+ tech, isProvisioned);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Indicates whether provisioning for the MMTEL capability and IMS registration technology
+ * specified is required or not
+ *
+ * <p> Requires Permission:
+ * <ul>
+ * <li> android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE,</li>
+ * <li>{@link android.Manifest.permission#READ_PRECISE_PHONE_STATE},</li>
+ * <li> or that the caller has carrier privileges (see
+ * {@link TelephonyManager#hasCarrierPrivileges()}).</li>
+ * </ul>
+ *
+ * @return true if provisioning is required for the MMTEL capability and IMS
+ * registration technology specified, false if it is not required.
+ */
+ @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
+ public boolean isProvisioningRequiredForCapability(
+ @MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
+ @ImsRegistrationImplBase.ImsRegistrationTech int tech) {
+ try {
+ return getITelephony().isProvisioningRequiredForCapability(mSubId, capability, tech);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+
+ /**
+ * Indicates whether provisioning for the RCS capability and IMS registration technology
+ * specified is required or not
+ *
+ * <p> Requires Permission:
+ * <ul>
+ * <li> android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE,</li>
+ * <li>{@link android.Manifest.permission#READ_PRECISE_PHONE_STATE},</li>
+ * <li> or that the caller has carrier privileges (see
+ * {@link TelephonyManager#hasCarrierPrivileges()}).</li>
+ * </ul>
+ *
+ * @return true if provisioning is required for the RCS capability and IMS
+ * registration technology specified, false if it is not required.
+ */
+ @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
+ public boolean isRcsProvisioningRequiredForCapability(
+ @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability,
+ @ImsRegistrationImplBase.ImsRegistrationTech int tech) {
+ try {
+ return getITelephony().isRcsProvisioningRequiredForCapability(mSubId, capability, tech);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+
+ /**
* Notify the framework that an RCS autoconfiguration XML file has been received for
* provisioning.
- * <p>
- * Requires Permission: Manifest.permission.MODIFY_PHONE_STATE or that the calling app has
- * carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
+ *
+ * <p>Requires Permission:
+ * <ul>
+ * <li>{@link Manifest.permission#MODIFY_PHONE_STATE},</li>
+ * <li>or that the calling app has carrier privileges (see
+ * {@link TelephonyManager#hasCarrierPrivileges()}).</li>
+ * </ul>
+ *
* @param config The XML file to be read. ASCII/UTF8 encoded text if not compressed.
* @param isCompressed The XML file is compressed in gzip format and must be decompressed
* before being read.
- *
+ * @hide
*/
+ @SystemApi
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
public void notifyRcsAutoConfigurationReceived(@NonNull byte[] config, boolean isCompressed) {
if (config == null) {
throw new IllegalArgumentException("Must include a non-null config XML file.");
}
+
try {
getITelephony().notifyRcsAutoConfigurationReceived(mSubId, config, isCompressed);
} catch (RemoteException e) {
@@ -1374,7 +1727,9 @@ public class ProvisioningManager {
* <p>Contains {@link #EXTRA_SUBSCRIPTION_ID} to specify the subscription index for which
* the intent is valid. and {@link #EXTRA_STATUS} to specify RCS VoLTE single registration
* status.
+ * @hide
*/
+ @SystemApi
@RequiresPermission(Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION)
@SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_RCS_SINGLE_REGISTRATION_CAPABILITY_UPDATE =
@@ -1382,7 +1737,9 @@ public class ProvisioningManager {
/**
* Integer extra to specify subscription index.
+ * @hide
*/
+ @SystemApi
public static final String EXTRA_SUBSCRIPTION_ID =
"android.telephony.ims.extra.SUBSCRIPTION_ID";
@@ -1392,22 +1749,30 @@ public class ProvisioningManager {
* <p>The value can be {@link #STATUS_CAPABLE}, {@link #STATUS_DEVICE_NOT_CAPABLE},
* {@link #STATUS_CARRIER_NOT_CAPABLE}, or bitwise OR of
* {@link #STATUS_DEVICE_NOT_CAPABLE} and {@link #STATUS_CARRIER_NOT_CAPABLE}.
+ * @hide
*/
+ @SystemApi
public static final String EXTRA_STATUS = "android.telephony.ims.extra.STATUS";
/**
* RCS VoLTE single registration is supported by the device and carrier.
+ * @hide
*/
+ @SystemApi
public static final int STATUS_CAPABLE = 0;
/**
* RCS VoLTE single registration is not supported by the device.
+ * @hide
*/
+ @SystemApi
public static final int STATUS_DEVICE_NOT_CAPABLE = 0x01;
/**
* RCS VoLTE single registration is not supported by the carrier
+ * @hide
*/
+ @SystemApi
public static final int STATUS_CARRIER_NOT_CAPABLE = 0x01 << 1;
/**
@@ -1417,11 +1782,14 @@ public class ProvisioningManager {
* provisioning is done using autoconfiguration, then these parameters shall be
* sent in the HTTP get request to fetch the RCS provisioning. RCS client
* configuration must be provided by the application before registering for the
- * provisioning status events {@link #registerRcsProvisioningCallback()}
+ * provisioning status events
+ * {@link #registerRcsProvisioningCallback(Executor, RcsProvisioningCallback)}
* When the IMS/RCS service receives the RCS client configuration, it will detect
* the change in the configuration, and trigger the auto-configuration as needed.
* @param rcc RCS client configuration {@link RcsClientConfiguration}
+ * @hide
*/
+ @SystemApi
@RequiresPermission(Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION)
public void setRcsClientConfiguration(
@NonNull RcsClientConfiguration rcc) throws ImsException {
@@ -1442,18 +1810,21 @@ public class ProvisioningManager {
* <ul>
* <li>{@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE},</li>
* <li>{@link android.Manifest.permission#PERFORM_IMS_SINGLE_REGISTRATION},</li>
- * <li>or that the caller has carrier privileges (see
+ * <li>or that the calling app has carrier privileges (see
* {@link TelephonyManager#hasCarrierPrivileges()}).</li>
* </ul>
+ *
* @return true if IMS single registration is capable at this time, or false otherwise
- * @throws ImsException If the remote ImsService is not available for
- * any reason or the subscription associated with this instance is no
- * longer active. See {@link ImsException#getCode()} for more
- * information.
+ * @throws ImsException If the remote ImsService is not available for any reason or
+ * the subscription associated with this instance is no longer active.
+ * See {@link ImsException#getCode()} for more information.
* @see PackageManager#FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION for whether or not this
* device supports IMS single registration.
+ * @hide
*/
- @RequiresPermission(anyOf = {Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ @SystemApi
+ @RequiresPermission(anyOf = {
+ Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION})
public boolean isRcsVolteSingleRegistrationCapable() throws ImsException {
try {
@@ -1480,8 +1851,6 @@ public class ProvisioningManager {
* <ul>
* <li>{@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE},</li>
* <li>{@link android.Manifest.permission#PERFORM_IMS_SINGLE_REGISTRATION},</li>
- * <li>or that the caller has carrier privileges (see
- * {@link TelephonyManager#hasCarrierPrivileges()}).</li>
* </ul>
*
* @param executor The {@link Executor} to call the callback methods on
@@ -1499,8 +1868,11 @@ public class ProvisioningManager {
* params (See {@link #setRcsClientConfiguration}) and re register the
* callback.
* See {@link ImsException#getCode()} for a more detailed reason.
+ * @hide
*/
- @RequiresPermission(anyOf = {Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ @SystemApi
+ @RequiresPermission(anyOf = {
+ Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION})
public void registerRcsProvisioningCallback(
@NonNull @CallbackExecutor Executor executor,
@@ -1527,8 +1899,6 @@ public class ProvisioningManager {
* <ul>
* <li>{@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE},</li>
* <li>{@link android.Manifest.permission#PERFORM_IMS_SINGLE_REGISTRATION},</li>
- * <li>or that the caller has carrier privileges (see
- * {@link TelephonyManager#hasCarrierPrivileges()}).</li>
* </ul>
*
* @param callback The existing {@link RcsProvisioningCallback} to be
@@ -1536,8 +1906,11 @@ public class ProvisioningManager {
* @see #registerRcsProvisioningCallback(Executor, RcsProvisioningCallback)
* @throws IllegalArgumentException if the subscription associated with
* this callback is invalid.
+ * @hide
*/
- @RequiresPermission(anyOf = {Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ @SystemApi
+ @RequiresPermission(anyOf = {
+ Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION})
public void unregisterRcsProvisioningCallback(
@NonNull RcsProvisioningCallback callback) {
@@ -1558,9 +1931,10 @@ public class ProvisioningManager {
* {@link RcsProvisioningCallback} may expect to receive
* {@link RcsProvisioningCallback#onConfigurationReset}, then
* {@link RcsProvisioningCallback#onConfigurationChanged} when the new
- * RCS configuration is received and notified by
- * {@link #notifyRcsAutoConfigurationReceived}
+ * RCS configuration is received and notified by {@link #notifyRcsAutoConfigurationReceived}
+ * @hide
*/
+ @SystemApi
@RequiresPermission(Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION)
public void triggerRcsReconfiguration() {
try {
diff --git a/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java b/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java
index 9c28c36521f5..6a6c3063483e 100644
--- a/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java
+++ b/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java
@@ -439,13 +439,13 @@ public final class RcsContactPresenceTuple implements Parcelable {
}
private RcsContactPresenceTuple(Parcel in) {
- mContactUri = in.readParcelable(Uri.class.getClassLoader());
+ mContactUri = in.readParcelable(Uri.class.getClassLoader(), android.net.Uri.class);
mTimestamp = convertStringFormatTimeToInstant(in.readString());
mStatus = in.readString();
mServiceId = in.readString();
mServiceVersion = in.readString();
mServiceDescription = in.readString();
- mServiceCapabilities = in.readParcelable(ServiceCapabilities.class.getClassLoader());
+ mServiceCapabilities = in.readParcelable(ServiceCapabilities.class.getClassLoader(), android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities.class);
}
@Override
diff --git a/telephony/java/android/telephony/ims/RcsContactTerminatedReason.java b/telephony/java/android/telephony/ims/RcsContactTerminatedReason.java
index ee02564267c0..ea022de3bc01 100644
--- a/telephony/java/android/telephony/ims/RcsContactTerminatedReason.java
+++ b/telephony/java/android/telephony/ims/RcsContactTerminatedReason.java
@@ -37,7 +37,7 @@ public final class RcsContactTerminatedReason implements Parcelable {
}
private RcsContactTerminatedReason(Parcel in) {
- mContactUri = in.readParcelable(Uri.class.getClassLoader());
+ mContactUri = in.readParcelable(Uri.class.getClassLoader(), android.net.Uri.class);
mReason = in.readString();
}
diff --git a/telephony/java/android/telephony/ims/RcsContactUceCapability.java b/telephony/java/android/telephony/ims/RcsContactUceCapability.java
index 91121187a19a..0f1b3695270b 100644
--- a/telephony/java/android/telephony/ims/RcsContactUceCapability.java
+++ b/telephony/java/android/telephony/ims/RcsContactUceCapability.java
@@ -244,14 +244,14 @@ public final class RcsContactUceCapability implements Parcelable {
}
private RcsContactUceCapability(Parcel in) {
- mContactUri = in.readParcelable(Uri.class.getClassLoader());
+ mContactUri = in.readParcelable(Uri.class.getClassLoader(), android.net.Uri.class);
mCapabilityMechanism = in.readInt();
mSourceType = in.readInt();
mRequestResult = in.readInt();
List<String> featureTagList = new ArrayList<>();
in.readStringList(featureTagList);
mFeatureTags.addAll(featureTagList);
- in.readParcelableList(mPresenceTuples, RcsContactPresenceTuple.class.getClassLoader());
+ in.readParcelableList(mPresenceTuples, RcsContactPresenceTuple.class.getClassLoader(), android.telephony.ims.RcsContactPresenceTuple.class);
}
@Override
diff --git a/telephony/java/android/telephony/ims/RtpHeaderExtensionType.java b/telephony/java/android/telephony/ims/RtpHeaderExtensionType.java
index af4e23476331..b9ffd247f658 100644
--- a/telephony/java/android/telephony/ims/RtpHeaderExtensionType.java
+++ b/telephony/java/android/telephony/ims/RtpHeaderExtensionType.java
@@ -63,7 +63,7 @@ public final class RtpHeaderExtensionType implements Parcelable {
private RtpHeaderExtensionType(Parcel in) {
mLocalIdentifier = in.readInt();
- mUri = in.readParcelable(Uri.class.getClassLoader());
+ mUri = in.readParcelable(Uri.class.getClassLoader(), android.net.Uri.class);
}
public static final @NonNull Creator<RtpHeaderExtensionType> CREATOR =
diff --git a/telephony/java/android/telephony/ims/SipDelegateConfiguration.java b/telephony/java/android/telephony/ims/SipDelegateConfiguration.java
index 1bf5cad49c53..db0ae033713e 100644
--- a/telephony/java/android/telephony/ims/SipDelegateConfiguration.java
+++ b/telephony/java/android/telephony/ims/SipDelegateConfiguration.java
@@ -573,7 +573,7 @@ public final class SipDelegateConfiguration implements Parcelable {
mPrivateUserIdentifier = source.readString();
mHomeDomain = source.readString();
mImei = source.readString();
- mGruu = source.readParcelable(null);
+ mGruu = source.readParcelable(null, android.net.Uri.class);
mSipAuthHeader = source.readString();
mSipAuthNonce = source.readString();
mServiceRouteHeader = source.readString();
diff --git a/telephony/java/android/telephony/ims/aidl/IFeatureProvisioningCallback.aidl b/telephony/java/android/telephony/ims/aidl/IFeatureProvisioningCallback.aidl
new file mode 100644
index 000000000000..63ec4aaf1f48
--- /dev/null
+++ b/telephony/java/android/telephony/ims/aidl/IFeatureProvisioningCallback.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.telephony.ims.aidl;
+
+/**
+ * Provides callback interface for FeatureProvisioning when a value has changed.
+ *
+ * {@hide}
+ */
+oneway interface IFeatureProvisioningCallback {
+ void onFeatureProvisioningChanged(int capability, int tech, boolean isProvisioned);
+ void onRcsFeatureProvisioningChanged(int capability, int tech, boolean isProvisioned);
+}
diff --git a/telephony/java/android/telephony/ims/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
index 7fdf21b3e5ff..ad2e9e133a11 100644
--- a/telephony/java/android/telephony/ims/feature/MmTelFeature.java
+++ b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
@@ -394,6 +394,13 @@ public class MmTelFeature extends ImsFeature {
public @interface MmTelCapability {}
/**
+ * Undefined capability type for initialization
+ * This is used to check the upper range of MmTel capability
+ * {@hide}
+ */
+ public static final int CAPABILITY_TYPE_NONE = 0;
+
+ /**
* This MmTelFeature supports Voice calling (IR.92)
*/
public static final int CAPABILITY_TYPE_VOICE = 1 << 0;
@@ -419,6 +426,12 @@ public class MmTelFeature extends ImsFeature {
public static final int CAPABILITY_TYPE_CALL_COMPOSER = 1 << 4;
/**
+ * This is used to check the upper range of MmTel capability
+ * {@hide}
+ */
+ public static final int CAPABILITY_TYPE_MAX = CAPABILITY_TYPE_CALL_COMPOSER + 1;
+
+ /**
* @hide
*/
@Override
diff --git a/telephony/java/android/telephony/ims/feature/RcsFeature.java b/telephony/java/android/telephony/ims/feature/RcsFeature.java
index 11cf0e3f7855..70e4ef1f1a3a 100644
--- a/telephony/java/android/telephony/ims/feature/RcsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/RcsFeature.java
@@ -59,9 +59,7 @@ import java.util.function.Supplier;
/**
* Base implementation of the RcsFeature APIs. Any ImsService wishing to support RCS should extend
* this class and provide implementations of the RcsFeature methods that they support.
- * @hide
*/
-@SystemApi
public class RcsFeature extends ImsFeature {
private static final String LOG_TAG = "RcsFeature";
@@ -186,14 +184,14 @@ public class RcsFeature extends ImsFeature {
* Contains the capabilities defined and supported by a {@link RcsFeature} in the
* form of a bitmask. The capabilities that are used in the RcsFeature are
* defined as:
- * {@link RcsUceAdatper.RcsImsCapabilityFlag#CAPABILITY_TYPE_OPTIONS_UCE}
- * {@link RceUceAdapter.RcsImsCapabilityFlag#CAPABILITY_TYPE_PRESENCE_UCE}
+ * {RcsUceAdapter.RcsImsCapabilityFlag#CAPABILITY_TYPE_OPTIONS_UCE}
+ * {RcsUceAdapter.RcsImsCapabilityFlag#CAPABILITY_TYPE_PRESENCE_UCE}
*
* The enabled capabilities of this RcsFeature will be set by the framework
- * using {@link #changeEnabledCapabilities(CapabilityChangeRequest, CapabilityCallbackProxy)}.
+ * using {#changeEnabledCapabilities(CapabilityChangeRequest, CapabilityCallbackProxy)}.
* After the capabilities have been set, the RcsFeature may then perform the necessary bring up
* of the capability and notify the capability status as true using
- * {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)}. This will signal to the
+ * {#notifyCapabilitiesStatusChanged(RcsImsCapabilities)}. This will signal to the
* framework that the capability is available for usage.
*/
public static class RcsImsCapabilities extends Capabilities {
@@ -227,10 +225,18 @@ public class RcsFeature extends ImsFeature {
public static final int CAPABILITY_TYPE_PRESENCE_UCE = 1 << 1;
/**
+ * This is used to check the upper range of RCS capability
+ * {@hide}
+ */
+ public static final int CAPABILITY_TYPE_MAX = CAPABILITY_TYPE_PRESENCE_UCE + 1;
+
+ /**
* Create a new {@link RcsImsCapabilities} instance with the provided capabilities.
* @param capabilities The capabilities that are supported for RCS in the form of a
* bitfield.
+ * @hide
*/
+ @SystemApi
public RcsImsCapabilities(@RcsUceAdapter.RcsImsCapabilityFlag int capabilities) {
super(capabilities);
}
@@ -243,17 +249,29 @@ public class RcsFeature extends ImsFeature {
super(capabilities.getMask());
}
+ /**
+ * @hide
+ */
@Override
+ @SystemApi
public void addCapabilities(@RcsUceAdapter.RcsImsCapabilityFlag int capabilities) {
super.addCapabilities(capabilities);
}
+ /**
+ * @hide
+ */
@Override
+ @SystemApi
public void removeCapabilities(@RcsUceAdapter.RcsImsCapabilityFlag int capabilities) {
super.removeCapabilities(capabilities);
}
+ /**
+ * @hide
+ */
@Override
+ @SystemApi
public boolean isCapable(@RcsUceAdapter.RcsImsCapabilityFlag int capabilities) {
return super.isCapable(capabilities);
}
@@ -270,7 +288,9 @@ public class RcsFeature extends ImsFeature {
* Method stubs called from the framework will be called asynchronously. To specify the
* {@link Executor} that the methods stubs will be called, use
* {@link RcsFeature#RcsFeature(Executor)} instead.
+ * @hide
*/
+ @SystemApi
public RcsFeature() {
super();
// Run on the Binder threads that call them.
@@ -282,7 +302,9 @@ public class RcsFeature extends ImsFeature {
* framework.
* @param executor The executor for the framework to use when executing the methods overridden
* by the implementation of RcsFeature.
+ * @hide
*/
+ @SystemApi
public RcsFeature(@NonNull Executor executor) {
super();
if (executor == null) {
@@ -301,7 +323,7 @@ public class RcsFeature extends ImsFeature {
* @hide
*/
@Override
- public void initialize(Context context, int slotId) {
+ public void initialize(@NonNull Context context, @NonNull int slotId) {
super.initialize(context, slotId);
// Notify that the RcsFeature is ready.
mExecutor.execute(() -> onFeatureReady());
@@ -313,8 +335,10 @@ public class RcsFeature extends ImsFeature {
* requests. To change the status of the capabilities
* {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)} should be called.
* @return A copy of the current RcsFeature capability status.
+ * @hide
*/
@Override
+ @SystemApi
public @NonNull final RcsImsCapabilities queryCapabilityStatus() {
return new RcsImsCapabilities(super.queryCapabilityStatus());
}
@@ -324,7 +348,9 @@ public class RcsFeature extends ImsFeature {
* this signals to the framework that the capability has been initialized and is ready.
* Call {@link #queryCapabilityStatus()} to return the current capability status.
* @param capabilities The current capability status of the RcsFeature.
+ * @hide
*/
+ @SystemApi
public final void notifyCapabilitiesStatusChanged(@NonNull RcsImsCapabilities capabilities) {
if (capabilities == null) {
throw new IllegalArgumentException("RcsImsCapabilities must be non-null!");
@@ -341,7 +367,9 @@ public class RcsFeature extends ImsFeature {
* @param capability The capability that we are querying the configuration for.
* @param radioTech The radio technology type that we are querying.
* @return true if the capability is enabled, false otherwise.
+ * @hide
*/
+ @SystemApi
public boolean queryCapabilityConfiguration(
@RcsUceAdapter.RcsImsCapabilityFlag int capability,
@ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
@@ -364,8 +392,10 @@ public class RcsFeature extends ImsFeature {
* be called for each capability change that resulted in an error.
* @param request The request to change the capability.
* @param callback To notify the framework that the result of the capability changes.
+ * @hide
*/
@Override
+ @SystemApi
public void changeEnabledCapabilities(@NonNull CapabilityChangeRequest request,
@NonNull CapabilityCallbackProxy callback) {
// Base Implementation - Override to provide functionality
@@ -385,7 +415,9 @@ public class RcsFeature extends ImsFeature {
* event to the framework.
* @return An instance of {@link RcsCapabilityExchangeImplBase} that implements capability
* exchange if it is supported by the device.
+ * @hide
*/
+ @SystemApi
public @NonNull RcsCapabilityExchangeImplBase createCapabilityExchangeImpl(
@NonNull CapabilityExchangeEventListener listener) {
// Base Implementation, override to implement functionality
@@ -395,20 +427,28 @@ public class RcsFeature extends ImsFeature {
/**
* Remove the given CapabilityExchangeImplBase instance.
* @param capExchangeImpl The {@link RcsCapabilityExchangeImplBase} instance to be destroyed.
+ * @hide
*/
+ @SystemApi
public void destroyCapabilityExchangeImpl(
@NonNull RcsCapabilityExchangeImplBase capExchangeImpl) {
// Override to implement the process of destroying RcsCapabilityExchangeImplBase instance.
}
- /**{@inheritDoc}*/
+ /**{@inheritDoc}
+ * @hide
+ */
@Override
+ @SystemApi
public void onFeatureRemoved() {
}
- /**{@inheritDoc}*/
+ /**{@inheritDoc}
+ * @hide
+ */
@Override
+ @SystemApi
public void onFeatureReady() {
}
diff --git a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
index 3b151a422b57..ac5565bea810 100644
--- a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
@@ -34,7 +34,6 @@ import com.android.internal.telephony.util.RemoteCallbackListExt;
import com.android.internal.telephony.util.TelephonyUtils;
import com.android.internal.util.ArrayUtils;
-
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.concurrent.CancellationException;
@@ -51,9 +50,7 @@ import java.util.function.Supplier;
* <p>
* Note: There is no guarantee on the thread that the calls from the framework will be called on. It
* is the implementors responsibility to handle moving the calls to a working thread if required.
- * @hide
*/
-@SystemApi
public class ImsRegistrationImplBase {
private static final String LOG_TAG = "ImsRegistrationImplBase";
@@ -94,6 +91,12 @@ public class ImsRegistrationImplBase {
*/
public static final int REGISTRATION_TECH_NR = 3;
+ /**
+ * This is used to check the upper range of registration tech
+ * {@hide}
+ */
+ public static final int REGISTRATION_TECH_MAX = REGISTRATION_TECH_NR + 1;
+
// Registration states, used to notify new ImsRegistrationImplBase#Callbacks of the current
// state.
// The unknown state is set as the initialization state. This is so that we do not call back
@@ -109,7 +112,9 @@ public class ImsRegistrationImplBase {
* Method stubs called from the framework will be called asynchronously. To specify the
* {@link Executor} that the methods stubs will be called, use
* {@link ImsRegistrationImplBase#ImsRegistrationImplBase(Executor)} instead.
+ * @hide This API is not part of the Android public SDK API
*/
+ @SystemApi
public ImsRegistrationImplBase() {
super();
}
@@ -119,7 +124,9 @@ public class ImsRegistrationImplBase {
* framework.
* @param executor The executor for the framework to use when executing the methods overridden
* by the implementation of ImsRegistration.
+ * @hide This API is not part of the Android public SDK API
*/
+ @SystemApi
public ImsRegistrationImplBase(@NonNull Executor executor) {
super();
mExecutor = executor;
@@ -250,7 +257,9 @@ public class ImsRegistrationImplBase {
* If the SIP delegate feature tag configuration has changed, then this method will be
* called in order to let the ImsService know that it can pick up these changes in the IMS
* registration.
+ * @hide This API is not part of the Android public SDK API
*/
+ @SystemApi
public void updateSipDelegateRegistration() {
// Stub implementation, ImsService should implement this
}
@@ -266,7 +275,9 @@ public class ImsRegistrationImplBase {
* <p>
* This should not affect the registration of features managed by the ImsService itself, such as
* feature tags related to MMTEL registration.
+ * @hide This API is not part of the Android public SDK API
*/
+ @SystemApi
public void triggerSipDelegateDeregistration() {
// Stub implementation, ImsService should implement this
}
@@ -284,7 +295,9 @@ public class ImsRegistrationImplBase {
* be carrier specific.
* @param sipReason The reason associated with the SIP error code. {@code null} if there was no
* reason associated with the error.
+ * @hide This API is not part of the Android public SDK API
*/
+ @SystemApi
public void triggerFullNetworkRegistration(@IntRange(from = 100, to = 699) int sipCode,
@Nullable String sipReason) {
// Stub implementation, ImsService should implement this
@@ -295,7 +308,9 @@ public class ImsRegistrationImplBase {
* Notify the framework that the device is connected to the IMS network.
*
* @param imsRadioTech the radio access technology.
+ * @hide This API is not part of the Android public SDK API
*/
+ @SystemApi
public final void onRegistered(@ImsRegistrationTech int imsRadioTech) {
onRegistered(new ImsRegistrationAttributes.Builder(imsRadioTech).build());
}
@@ -304,7 +319,9 @@ public class ImsRegistrationImplBase {
* Notify the framework that the device is connected to the IMS network.
*
* @param attributes The attributes associated with the IMS registration.
+ * @hide This API is not part of the Android public SDK API
*/
+ @SystemApi
public final void onRegistered(@NonNull ImsRegistrationAttributes attributes) {
updateToState(attributes, RegistrationManager.REGISTRATION_STATE_REGISTERED);
mCallbacks.broadcastAction((c) -> {
@@ -320,7 +337,9 @@ public class ImsRegistrationImplBase {
* Notify the framework that the device is trying to connect the IMS network.
*
* @param imsRadioTech the radio access technology.
+ * @hide This API is not part of the Android public SDK API
*/
+ @SystemApi
public final void onRegistering(@ImsRegistrationTech int imsRadioTech) {
onRegistering(new ImsRegistrationAttributes.Builder(imsRadioTech).build());
}
@@ -329,7 +348,9 @@ public class ImsRegistrationImplBase {
* Notify the framework that the device is trying to connect the IMS network.
*
* @param attributes The attributes associated with the IMS registration.
+ * @hide This API is not part of the Android public SDK API
*/
+ @SystemApi
public final void onRegistering(@NonNull ImsRegistrationAttributes attributes) {
updateToState(attributes, RegistrationManager.REGISTRATION_STATE_REGISTERING);
mCallbacks.broadcastAction((c) -> {
@@ -356,7 +377,9 @@ public class ImsRegistrationImplBase {
* result.
*
* @param info the {@link ImsReasonInfo} associated with why registration was disconnected.
+ * @hide This API is not part of the Android public SDK API
*/
+ @SystemApi
public final void onDeregistered(ImsReasonInfo info) {
updateToDisconnectedState(info);
// ImsReasonInfo should never be null.
@@ -377,7 +400,9 @@ public class ImsRegistrationImplBase {
* {@link #REGISTRATION_TECH_LTE}, {@link #REGISTRATION_TECH_IWLAN} and
* {@link #REGISTRATION_TECH_CROSS_SIM}.
* @param info The {@link ImsReasonInfo} for the failure to change technology.
+ * @hide This API is not part of the Android public SDK API
*/
+ @SystemApi
public final void onTechnologyChangeFailed(@ImsRegistrationTech int imsRadioTech,
ImsReasonInfo info) {
final ImsReasonInfo reasonInfo = (info != null) ? info : new ImsReasonInfo();
@@ -396,7 +421,9 @@ public class ImsRegistrationImplBase {
*
* The {@link Uri}s are not guaranteed to be different between subsequent calls.
* @param uris changed uris
+ * @hide This API is not part of the Android public SDK API
*/
+ @SystemApi
public final void onSubscriberAssociatedUriChanged(Uri[] uris) {
synchronized (mLock) {
mUris = ArrayUtils.cloneOrNull(uris);
diff --git a/telephony/java/android/telephony/mbms/DownloadRequest.java b/telephony/java/android/telephony/mbms/DownloadRequest.java
index eb59f87a6c02..81d5be8562b6 100644
--- a/telephony/java/android/telephony/mbms/DownloadRequest.java
+++ b/telephony/java/android/telephony/mbms/DownloadRequest.java
@@ -242,8 +242,8 @@ public final class DownloadRequest implements Parcelable {
private DownloadRequest(Parcel in) {
fileServiceId = in.readString();
- sourceUri = in.readParcelable(getClass().getClassLoader());
- destinationUri = in.readParcelable(getClass().getClassLoader());
+ sourceUri = in.readParcelable(getClass().getClassLoader(), android.net.Uri.class);
+ destinationUri = in.readParcelable(getClass().getClassLoader(), android.net.Uri.class);
subscriptionId = in.readInt();
serializedResultIntentForApp = in.readString();
version = in.readInt();
diff --git a/telephony/java/android/telephony/mbms/FileInfo.java b/telephony/java/android/telephony/mbms/FileInfo.java
index e52b2ce0c505..ffd864ebda93 100644
--- a/telephony/java/android/telephony/mbms/FileInfo.java
+++ b/telephony/java/android/telephony/mbms/FileInfo.java
@@ -55,7 +55,7 @@ public final class FileInfo implements Parcelable {
}
private FileInfo(Parcel in) {
- uri = in.readParcelable(null);
+ uri = in.readParcelable(null, android.net.Uri.class);
mimeType = in.readString();
}
diff --git a/telephony/java/android/telephony/mbms/FileServiceInfo.java b/telephony/java/android/telephony/mbms/FileServiceInfo.java
index 8777e7f59e3f..0fc3be6de929 100644
--- a/telephony/java/android/telephony/mbms/FileServiceInfo.java
+++ b/telephony/java/android/telephony/mbms/FileServiceInfo.java
@@ -58,7 +58,7 @@ public final class FileServiceInfo extends ServiceInfo implements Parcelable {
FileServiceInfo(Parcel in) {
super(in);
files = new ArrayList<FileInfo>();
- in.readList(files, FileInfo.class.getClassLoader());
+ in.readList(files, FileInfo.class.getClassLoader(), android.telephony.mbms.FileInfo.class);
}
@Override
diff --git a/telephony/java/android/telephony/mbms/ServiceInfo.java b/telephony/java/android/telephony/mbms/ServiceInfo.java
index f78e7a6e54c4..02424ff75c82 100644
--- a/telephony/java/android/telephony/mbms/ServiceInfo.java
+++ b/telephony/java/android/telephony/mbms/ServiceInfo.java
@@ -80,7 +80,7 @@ public class ServiceInfo {
}
names = new HashMap(mapCount);
while (mapCount-- > 0) {
- Locale locale = (java.util.Locale) in.readSerializable();
+ Locale locale = (java.util.Locale) in.readSerializable(java.util.Locale.class.getClassLoader(), java.util.Locale.class);
String name = in.readString();
names.put(locale, name);
}
@@ -91,12 +91,12 @@ public class ServiceInfo {
}
locales = new ArrayList<Locale>(localesCount);
while (localesCount-- > 0) {
- Locale l = (java.util.Locale) in.readSerializable();
+ Locale l = (java.util.Locale) in.readSerializable(java.util.Locale.class.getClassLoader(), java.util.Locale.class);
locales.add(l);
}
serviceId = in.readString();
- sessionStartTime = (java.util.Date) in.readSerializable();
- sessionEndTime = (java.util.Date) in.readSerializable();
+ sessionStartTime = (java.util.Date) in.readSerializable(java.util.Date.class.getClassLoader(), java.util.Date.class);
+ sessionEndTime = (java.util.Date) in.readSerializable(java.util.Date.class.getClassLoader(), java.util.Date.class);
}
/** @hide */
diff --git a/telephony/java/android/telephony/mbms/UriPathPair.java b/telephony/java/android/telephony/mbms/UriPathPair.java
index 9258919919b7..54d9d9e5284e 100644
--- a/telephony/java/android/telephony/mbms/UriPathPair.java
+++ b/telephony/java/android/telephony/mbms/UriPathPair.java
@@ -48,8 +48,8 @@ public final class UriPathPair implements Parcelable {
/** @hide */
private UriPathPair(Parcel in) {
- mFilePathUri = in.readParcelable(Uri.class.getClassLoader());
- mContentUri = in.readParcelable(Uri.class.getClassLoader());
+ mFilePathUri = in.readParcelable(Uri.class.getClassLoader(), android.net.Uri.class);
+ mContentUri = in.readParcelable(Uri.class.getClassLoader(), android.net.Uri.class);
}
public static final @android.annotation.NonNull Creator<UriPathPair> CREATOR = new Creator<UriPathPair>() {
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index be54cecbe3ba..bce7a246463d 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -55,6 +55,7 @@ import android.telephony.VisualVoicemailSmsFilterSettings;
import android.telephony.emergency.EmergencyNumber;
import android.telephony.ims.RcsClientConfiguration;
import android.telephony.ims.RcsContactUceCapability;
+import android.telephony.ims.aidl.IFeatureProvisioningCallback;
import android.telephony.ims.aidl.IImsCapabilityCallback;
import android.telephony.ims.aidl.IImsConfig;
import android.telephony.ims.aidl.IImsConfigCallback;
@@ -1992,6 +1993,18 @@ interface ITelephony {
void unregisterImsProvisioningChangedCallback(int subId, IImsConfigCallback callback);
/**
+ * Register an IMS provisioning change callback with Telephony.
+ */
+ void registerFeatureProvisioningChangedCallback(int subId,
+ IFeatureProvisioningCallback callback);
+
+ /**
+ * unregister an existing IMS provisioning change callback.
+ */
+ void unregisterFeatureProvisioningChangedCallback(int subId,
+ IFeatureProvisioningCallback callback);
+
+ /**
* Set the provisioning status for the IMS MmTel capability using the specified subscription.
*/
void setImsProvisioningStatusForCapability(int subId, int capability, int tech,
@@ -2005,19 +2018,12 @@ interface ITelephony {
/**
* Get the provisioning status for the IMS Rcs capability specified.
*/
- boolean getRcsProvisioningStatusForCapability(int subId, int capability);
+ boolean getRcsProvisioningStatusForCapability(int subId, int capability, int tech);
/**
* Set the provisioning status for the IMS Rcs capability using the specified subscription.
*/
- void setRcsProvisioningStatusForCapability(int subId, int capability,
- boolean isProvisioned);
-
- /** Is the capability and tech flagged as provisioned in the cache */
- boolean isMmTelCapabilityProvisionedInCache(int subId, int capability, int tech);
-
- /** Set the provisioning for the capability and tech in the cache */
- void cacheMmTelCapabilityProvisioning(int subId, int capability, int tech,
+ void setRcsProvisioningStatusForCapability(int subId, int capability, int tech,
boolean isProvisioned);
/**
@@ -2523,4 +2529,18 @@ interface ITelephony {
* @return the service name of the modem service which bind to.
*/
String getModemService();
+
+ /**
+ * Is Provisioning required for capability
+ * @return true if provisioning is required for the MMTEL capability and IMS
+ * registration technology specified, false if it is not required.
+ */
+ boolean isProvisioningRequiredForCapability(int subId, int capability, int tech);
+
+ /**
+ * Is RCS Provisioning required for capability
+ * @return true if provisioning is required for the RCS capability and IMS
+ * registration technology specified, false if it is not required.
+ */
+ boolean isRcsProvisioningRequiredForCapability(int subId, int capability, int tech);
}
diff --git a/telephony/java/com/android/internal/telephony/NetworkScanResult.java b/telephony/java/com/android/internal/telephony/NetworkScanResult.java
index d07d77ca742a..8b49f4b4593c 100644
--- a/telephony/java/com/android/internal/telephony/NetworkScanResult.java
+++ b/telephony/java/com/android/internal/telephony/NetworkScanResult.java
@@ -83,7 +83,7 @@ public final class NetworkScanResult implements Parcelable {
scanStatus = in.readInt();
scanError = in.readInt();
List<CellInfo> ni = new ArrayList<>();
- in.readParcelableList(ni, Object.class.getClassLoader());
+ in.readParcelableList(ni, Object.class.getClassLoader(), android.telephony.CellInfo.class);
networkInfos = ni;
}
diff --git a/telephony/java/com/android/internal/telephony/OperatorInfo.java b/telephony/java/com/android/internal/telephony/OperatorInfo.java
index a6f0f667d0cd..1820a1dc4d6c 100644
--- a/telephony/java/com/android/internal/telephony/OperatorInfo.java
+++ b/telephony/java/com/android/internal/telephony/OperatorInfo.java
@@ -189,7 +189,7 @@ public class OperatorInfo implements Parcelable {
in.readString(), /*operatorAlphaLong*/
in.readString(), /*operatorAlphaShort*/
in.readString(), /*operatorNumeric*/
- (State) in.readSerializable(), /*state*/
+ (State) in.readSerializable(com.android.internal.telephony.OperatorInfo.State.class.getClassLoader(), com.android.internal.telephony.OperatorInfo.State.class), /*state*/
in.readInt()); /*ran*/
return opInfo;
}
diff --git a/telephony/java/com/android/internal/telephony/uicc/IccUtils.java b/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
index 21c3f760771c..f518d53fa517 100644
--- a/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
+++ b/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
@@ -24,6 +24,7 @@ import android.graphics.Bitmap;
import android.graphics.Color;
import android.os.Build;
import android.telephony.UiccPortInfo;
+import android.text.TextUtils;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.GsmAlphabet;
@@ -934,6 +935,13 @@ public class IccUtils {
}
/**
+ * Strip all the trailing 'F' characters of a string if exists and compare.
+ */
+ public static boolean compareIgnoreTrailingFs(String a, String b) {
+ return TextUtils.equals(a, b) || TextUtils.equals(stripTrailingFs(a), stripTrailingFs(b));
+ }
+
+ /**
* Converts a character of [0-9a-fA-F] to its hex value in a byte. If the character is not a
* hex number, 0 will be returned.
*/
diff --git a/tests/DataIdleTest/src/com/android/tests/dataidle/DataIdleTest.java b/tests/DataIdleTest/src/com/android/tests/dataidle/DataIdleTest.java
index 4ec86b186285..56848b89be6f 100644
--- a/tests/DataIdleTest/src/com/android/tests/dataidle/DataIdleTest.java
+++ b/tests/DataIdleTest/src/com/android/tests/dataidle/DataIdleTest.java
@@ -15,20 +15,19 @@
*/
package com.android.tests.dataidle;
+import static android.net.NetworkStats.METERED_YES;
+
+import android.app.usage.NetworkStats;
+import android.app.usage.NetworkStatsManager;
import android.content.Context;
-import android.net.INetworkStatsService;
-import android.net.INetworkStatsSession;
-import android.net.NetworkStats;
-import android.net.NetworkStats.Entry;
import android.net.NetworkTemplate;
-import android.net.TrafficStats;
import android.os.Bundle;
-import android.os.RemoteException;
-import android.os.ServiceManager;
import android.telephony.TelephonyManager;
import android.test.InstrumentationTestCase;
import android.util.Log;
+import java.util.Set;
+
/**
* A test that dumps data usage to instrumentation out, used for measuring data usage for idle
* devices.
@@ -36,7 +35,7 @@ import android.util.Log;
public class DataIdleTest extends InstrumentationTestCase {
private TelephonyManager mTelephonyManager;
- private INetworkStatsService mStatsService;
+ private NetworkStatsManager mStatsManager;
private static final String LOG_TAG = "DataIdleTest";
private final static int INSTRUMENTATION_IN_PROGRESS = 2;
@@ -44,8 +43,7 @@ public class DataIdleTest extends InstrumentationTestCase {
protected void setUp() throws Exception {
super.setUp();
Context c = getInstrumentation().getTargetContext();
- mStatsService = INetworkStatsService.Stub.asInterface(
- ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
+ mStatsManager = c.getSystemService(NetworkStatsManager.class);
mTelephonyManager = (TelephonyManager) c.getSystemService(Context.TELEPHONY_SERVICE);
}
@@ -53,7 +51,9 @@ public class DataIdleTest extends InstrumentationTestCase {
* Test that dumps all the data usage metrics for wifi to instrumentation out.
*/
public void testWifiIdle() {
- NetworkTemplate template = NetworkTemplate.buildTemplateWifiWildcard();
+ final NetworkTemplate template = new NetworkTemplate
+ .Builder(NetworkTemplate.MATCH_WIFI)
+ .build();
fetchStats(template);
}
@@ -61,8 +61,11 @@ public class DataIdleTest extends InstrumentationTestCase {
* Test that dumps all the data usage metrics for all mobile to instrumentation out.
*/
public void testMobile() {
- String subscriberId = mTelephonyManager.getSubscriberId();
- NetworkTemplate template = NetworkTemplate.buildTemplateMobileAll(subscriberId);
+ final String subscriberId = mTelephonyManager.getSubscriberId();
+ NetworkTemplate template = new NetworkTemplate
+ .Builder(NetworkTemplate.MATCH_MOBILE)
+ .setMeteredness(METERED_YES)
+ .setSubscriberIds(Set.of(subscriberId)).build();
fetchStats(template);
}
@@ -72,49 +75,26 @@ public class DataIdleTest extends InstrumentationTestCase {
* @param template {@link NetworkTemplate} to match.
*/
private void fetchStats(NetworkTemplate template) {
- INetworkStatsSession session = null;
try {
- mStatsService.forceUpdate();
- session = mStatsService.openSession();
- final NetworkStats stats = session.getSummaryForAllUid(
- template, Long.MIN_VALUE, Long.MAX_VALUE, false);
- reportStats(stats);
- } catch (RemoteException e) {
+ mStatsManager.forceUpdate();
+ final NetworkStats.Bucket bucket =
+ mStatsManager.querySummaryForDevice(template, Long.MIN_VALUE, Long.MAX_VALUE);
+ reportStats(bucket);
+ } catch (RuntimeException e) {
Log.w(LOG_TAG, "Failed to fetch network stats.");
- } finally {
- TrafficStats.closeQuietly(session);
}
}
/**
* Print network data usage stats to instrumentation out
- * @param stats {@link NetworkorStats} to print
+ * @param bucket {@link NetworkStats} to print
*/
- void reportStats(NetworkStats stats) {
+ void reportStats(NetworkStats.Bucket bucket) {
Bundle result = new Bundle();
- long rxBytes = 0;
- long txBytes = 0;
- long rxPackets = 0;
- long txPackets = 0;
- for (int i = 0; i < stats.size(); ++i) {
- // Label will be iface_uid_tag_set
- Entry statsEntry = stats.getValues(i, null);
- // Debugging use.
- /*
- String labelTemplate = String.format("%s_%d_%d_%d", statsEntry.iface, statsEntry.uid,
- statsEntry.tag, statsEntry.set) + "_%s";
- result.putLong(String.format(labelTemplate, "rxBytes"), statsEntry.rxBytes);
- result.putLong(String.format(labelTemplate, "txBytes"), statsEntry.txBytes);
- */
- rxPackets += statsEntry.rxPackets;
- rxBytes += statsEntry.rxBytes;
- txPackets += statsEntry.txPackets;
- txBytes += statsEntry.txBytes;
- }
- result.putLong("Total rx Bytes", rxBytes);
- result.putLong("Total tx Bytes", txBytes);
- result.putLong("Total rx Packets", rxPackets);
- result.putLong("Total tx Packets", txPackets);
+ result.putLong("Total rx Bytes", bucket.getRxBytes());
+ result.putLong("Total tx Bytes", bucket.getTxBytes());
+ result.putLong("Total rx Packets", bucket.getRxPackets());
+ result.putLong("Total tx Packets", bucket.getTxPackets());
getInstrumentation().sendStatus(INSTRUMENTATION_IN_PROGRESS, result);
}
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 22acc03af6b6..d960e94bf09d 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
@@ -108,6 +108,15 @@ class CloseAppHomeButtonTest(testSpec: FlickerTestParameter) : CloseAppTransitio
super.entireScreenCovered()
}
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ // This test doesn't work in shell transitions because of b/215885246
+ assumeFalse(isShellTransitionsEnabled)
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+ }
+
companion object {
/**
* Creates the test configurations.
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/OWNERS b/tests/FlickerTests/src/com/android/server/wm/flicker/close/OWNERS
new file mode 100644
index 000000000000..f7c0a87f5dac
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/OWNERS
@@ -0,0 +1,2 @@
+# window manager > animations/transitions
+# Bug component: 316275
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt
index 0b1748a6bda4..535612a2bd8b 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt
@@ -17,7 +17,9 @@
package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
+import androidx.test.uiautomator.By
import androidx.test.uiautomator.UiDevice
+import androidx.test.uiautomator.Until
import com.android.server.wm.flicker.testapp.ActivityOptions
import com.android.server.wm.traces.common.FlickerComponentName
import com.android.server.wm.traces.parser.toFlickerComponent
@@ -47,4 +49,28 @@ class ImeAppAutoFocusHelper @JvmOverloads constructor(
}
launcherStrategy.launch(appName, expectedPackage)
}
+
+ fun startDialogThemedActivity(wmHelper: WindowManagerStateHelper) {
+ val button = uiDevice.wait(Until.findObject(By.res(getPackage(),
+ "start_dialog_themed_activity_btn")), FIND_TIMEOUT)
+
+ require(button != null) {
+ "Button not found, this usually happens when the device " +
+ "was left in an unknown state (e.g. Screen turned off)"
+ }
+ button.click()
+ wmHelper.waitForAppTransitionIdle()
+ wmHelper.waitForFullScreenApp(
+ ActivityOptions.DIALOG_THEMED_ACTIVITY_COMPONENT_NAME.toFlickerComponent())
+ }
+ fun dismissDialog(wmHelper: WindowManagerStateHelper) {
+ val dialog = uiDevice.wait(
+ Until.findObject(By.text("Dialog for test")), FIND_TIMEOUT)
+
+ // Pressing back key to dismiss the dialog
+ if (dialog != null) {
+ uiDevice.pressBack()
+ wmHelper.waitForAppTransitionIdle()
+ }
+ }
}
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 f2696d8a71ff..815ea778555b 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
@@ -178,7 +178,7 @@ class CloseImeAutoOpenWindowToHomeTest(private val testSpec: FlickerTestParamete
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(repetitions = 1,
+ .getConfigNonRotationTests(repetitions = 5,
// b/190352379 (IME doesn't show on app launch in 90 degrees)
supportedRotations = listOf(Surface.ROTATION_0),
supportedNavigationModes = listOf(
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt
new file mode 100644
index 000000000000..429541c28d19
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.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
+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.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
+import com.android.server.wm.traces.common.FlickerComponentName
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test IME snapshot mechanism won't apply when transitioning from non-IME focused dialog activity.
+ * To run this test: `atest FlickerTests:LaunchAppShowImeAndDialogThemeAppTest`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class LaunchAppShowImeAndDialogThemeAppTest(private val testSpec: FlickerTestParameter) {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val testApp = ImeAppAutoFocusHelper(instrumentation, testSpec.startRotation)
+
+ @FlickerBuilderProvider
+ fun buildFlicker(): FlickerBuilder {
+ return FlickerBuilder(instrumentation).apply {
+ setup {
+ eachRun {
+ testApp.launchViaIntent(wmHelper)
+ wmHelper.waitImeShown()
+ testApp.startDialogThemedActivity(wmHelper)
+ }
+ }
+ teardown {
+ eachRun {
+ testApp.exit()
+ }
+ }
+ transitions {
+ testApp.dismissDialog(wmHelper)
+ }
+ }
+ }
+
+ /**
+ * Checks that [FlickerComponentName.IME] layer becomes visible during the transition
+ */
+ @FlakyTest(bugId = 215884488)
+ @Test
+ fun imeWindowIsAlwaysVisible() = testSpec.imeWindowIsAlwaysVisible()
+
+ /**
+ * Checks that [FlickerComponentName.IME] layer is visible at the end of the transition
+ */
+ @Presubmit
+ @Test
+ fun imeLayerExistsEnd() {
+ testSpec.assertLayersEnd {
+ this.isVisible(FlickerComponentName.IME)
+ }
+ }
+
+ /**
+ * Checks that [FlickerComponentName.IME_SNAPSHOT] layer is invisible always.
+ */
+ @Presubmit
+ @Test
+ fun imeSnapshotNotVisible() {
+ testSpec.assertLayers {
+ this.isInvisible(FlickerComponentName.IME_SNAPSHOT)
+ }
+ }
+
+ companion object {
+ /**
+ * Creates the test configurations.
+ *
+ * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring
+ * repetitions, screen orientation and navigation modes.
+ */
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance()
+ .getConfigNonRotationTests(
+ repetitions = 3,
+ supportedRotations = listOf(Surface.ROTATION_0),
+ supportedNavigationModes = listOf(
+ WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY,
+ WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
+ )
+ )
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OWNERS b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OWNERS
new file mode 100644
index 000000000000..301fafa5309e
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OWNERS
@@ -0,0 +1,2 @@
+# ime
+# Bug component: 34867
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 0ad0a033a4fe..5e06f11fb6b8 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
@@ -18,8 +18,8 @@ package com.android.server.wm.flicker.ime
import android.app.Instrumentation
import android.platform.test.annotations.Presubmit
+import android.view.Display
import android.view.Surface
-import android.view.WindowManagerPolicyConstants
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
@@ -41,7 +41,9 @@ import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
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.common.ConditionList
import com.android.server.wm.traces.common.FlickerComponentName
+import com.android.server.wm.traces.common.WindowManagerConditionsFactory
import org.junit.Assume.assumeFalse
import org.junit.Assume.assumeTrue
import org.junit.FixMethodOrder
@@ -63,6 +65,12 @@ class ReOpenImeWindowTest(private val testSpec: FlickerTestParameter) {
private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
private val testApp = ImeAppAutoFocusHelper(instrumentation, testSpec.startRotation)
+ private val waitConditionSetup = ConditionList(listOf(
+ WindowManagerConditionsFactory.isAppTransitionIdle(Display.DEFAULT_DISPLAY),
+ WindowManagerConditionsFactory.hasLayersAnimating().negate(),
+ WindowManagerConditionsFactory.isHomeActivityVisible()
+ ))
+
@FlickerBuilderProvider
fun buildFlicker(): FlickerBuilder {
return FlickerBuilder(instrumentation).apply {
@@ -73,8 +81,7 @@ class ReOpenImeWindowTest(private val testSpec: FlickerTestParameter) {
}
eachRun {
device.pressRecentApps()
- wmHelper.waitImeGone()
- wmHelper.waitForAppTransitionIdle()
+ wmHelper.waitFor(waitConditionSetup)
this.setRotation(testSpec.startRotation)
}
}
@@ -231,11 +238,8 @@ class ReOpenImeWindowTest(private val testSpec: FlickerTestParameter) {
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
.getConfigNonRotationTests(
- repetitions = 1,
- supportedRotations = listOf(Surface.ROTATION_0),
- supportedNavigationModes = listOf(
- WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
- )
+ repetitions = 5,
+ supportedRotations = listOf(Surface.ROTATION_0)
)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OWNERS b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OWNERS
new file mode 100644
index 000000000000..2c414a27cacb
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OWNERS
@@ -0,0 +1,4 @@
+# System UI > ... > Overview (recent apps) > UI
+# Bug template url: https://b.corp.google.com/issues/new?component=807991&template=1390280 = per-file *Overview*
+# window manager > animations/transitions
+# Bug template url: https://b.corp.google.com/issues/new?component=316275&template=1018192
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 e07a8f94d651..5450610722fa 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
@@ -122,6 +122,11 @@ class OpenAppColdTest(testSpec: FlickerTestParameter) : OpenAppTransition(testSp
@Test
override fun navBarWindowIsVisible() = super.navBarWindowIsVisible()
+ /** {@inheritDoc} */
+ @FlakyTest(bugId = 213852103)
+ @Test
+ override fun entireScreenCovered() = super.entireScreenCovered()
+
companion object {
/**
* Creates the test configurations.
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
index 6e5c600eb141..a85dcc57ddb2 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
@@ -127,7 +127,7 @@ class OpenAppNonResizeableTest(testSpec: FlickerTestParameter) : OpenAppTransiti
* The `isAppWindowInvisible` step is optional because we log once per frame, upon logging,
* the window may be visible or not depending on what was processed until that moment.
*/
- @Presubmit
+ @FlakyTest(bugId = 203538234)
@Test
fun appWindowBecomesVisible() {
testSpec.assertWm {
@@ -240,7 +240,7 @@ class OpenAppNonResizeableTest(testSpec: FlickerTestParameter) : OpenAppTransiti
* it cannot use the regular assertion (check over time), because on lock screen neither
* the app not the launcher are visible, and there is no top visible window.
*/
- @Presubmit
+ @FlakyTest(bugId = 203538234)
@Test
override fun appWindowReplacesLauncherAsTopWindow() {
testSpec.assertWm {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/OWNERS b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/OWNERS
new file mode 100644
index 000000000000..897fe5dee7fb
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/OWNERS
@@ -0,0 +1,2 @@
+# System UI > ... > Launcher > Gesture nav
+# Bug component: 565144
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/OWNERS b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/OWNERS
new file mode 100644
index 000000000000..f7c0a87f5dac
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/OWNERS
@@ -0,0 +1,2 @@
+# window manager > animations/transitions
+# Bug component: 316275
diff --git a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
index 0a88f6bb5908..9e371e5e381e 100644
--- a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
@@ -97,5 +97,16 @@
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
+ <activity android:name=".DialogThemedActivity"
+ android:taskAffinity="com.android.server.wm.flicker.testapp.DialogThemedActivity"
+ android:configChanges="orientation|screenSize"
+ android:theme="@style/DialogTheme"
+ android:label="DialogThemedActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
</application>
</manifest>
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 2620ff407efc..baaf7073b3a6 100644
--- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml
@@ -31,4 +31,9 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Finish activity" />
+ <Button
+ android:id="@+id/start_dialog_themed_activity_btn"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="Start dialog themed activity" />
</LinearLayout>
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml b/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml
index 87a61a88c094..746b0f4ca7e1 100644
--- a/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml
@@ -27,4 +27,15 @@
<style name="CutoutNever">
<item name="android:windowLayoutInDisplayCutoutMode">never</item>
</style>
-</resources> \ No newline at end of file
+
+ <style name="DialogTheme" parent="@android:style/Theme.DeviceDefault">
+ <item name="android:windowAnimationStyle">@null</item>
+ <item name="android:windowIsTranslucent">true</item>
+ <item name="android:windowBackground">@null</item>
+ <item name="android:windowContentOverlay">@null</item>
+ <item name="android:windowNoTitle">true</item>
+ <item name="android:windowIsFloating">true</item>
+ <item name="android:backgroundDimEnabled">false</item>
+ <item name="android:windowSoftInputMode">stateUnchanged</item>
+ </style>
+</resources>
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
index baf36ab0e132..13adb681c30c 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
@@ -56,4 +56,8 @@ public class ActivityOptions {
public static final ComponentName LAUNCH_NEW_TASK_ACTIVITY_COMPONENT_NAME =
new ComponentName(FLICKER_APP_PACKAGE,
FLICKER_APP_PACKAGE + ".LaunchNewTaskActivity");
+ public static final String DIALOG_THEMED_ACTIVITY = "DialogThemedActivity";
+ public static final ComponentName DIALOG_THEMED_ACTIVITY_COMPONENT_NAME =
+ new ComponentName(FLICKER_APP_PACKAGE,
+ FLICKER_APP_PACKAGE + ".DialogThemedActivity");
}
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/DialogThemedActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/DialogThemedActivity.java
new file mode 100644
index 000000000000..27606d81f9d3
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/DialogThemedActivity.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 com.android.server.wm.flicker.testapp;
+
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
+import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.view.WindowManager;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+public class DialogThemedActivity extends Activity {
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_simple);
+ getWindow().getDecorView().setBackgroundColor(Color.TRANSPARENT);
+ TextView textView = new TextView(this);
+ textView.setText("This is a test dialog");
+ textView.setTextColor(Color.BLACK);
+ LinearLayout layout = new LinearLayout(this);
+ layout.setBackgroundColor(Color.GREEN);
+ layout.addView(textView);
+
+ // Create a dialog with dialog-themed activity
+ AlertDialog dialog = new AlertDialog.Builder(this)
+ .setView(layout)
+ .setTitle("Dialog for test")
+ .create();
+ final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(MATCH_PARENT,
+ MATCH_PARENT);
+ attrs.flags = FLAG_DIM_BEHIND | FLAG_ALT_FOCUSABLE_IM;
+ dialog.getWindow().getDecorView().setLayoutParams(attrs);
+ dialog.setCanceledOnTouchOutside(true);
+ dialog.show();
+ dialog.setOnDismissListener((d) -> finish());
+ }
+}
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeActivityAutoFocus.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeActivityAutoFocus.java
index 05da717620aa..bb200f125507 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeActivityAutoFocus.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeActivityAutoFocus.java
@@ -16,6 +16,8 @@
package com.android.server.wm.flicker.testapp;
+import android.content.Intent;
+import android.widget.Button;
import android.widget.EditText;
public class ImeActivityAutoFocus extends ImeActivity {
@@ -26,5 +28,9 @@ public class ImeActivityAutoFocus extends ImeActivity {
EditText editTextField = findViewById(R.id.plain_text_input);
editTextField.requestFocus();
+
+ Button startThemedActivityButton = findViewById(R.id.start_dialog_themed_activity_btn);
+ startThemedActivityButton.setOnClickListener(
+ button -> startActivity(new Intent(this, DialogThemedActivity.class)));
}
}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnTest.java b/tests/vcn/java/com/android/server/vcn/VcnTest.java
index 5d2f9d748581..6d269686e42f 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnTest.java
@@ -58,6 +58,7 @@ import android.util.ArraySet;
import com.android.server.VcnManagementService.VcnCallback;
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
import com.android.server.vcn.Vcn.VcnGatewayStatusCallback;
+import com.android.server.vcn.Vcn.VcnUserMobileDataStateListener;
import com.android.server.vcn.VcnNetworkProvider.NetworkRequestListener;
import org.junit.Before;
@@ -208,6 +209,13 @@ public class VcnTest {
}
@Test
+ public void testMobileDataStateListenersRegistered() {
+ // Validate state from setUp()
+ verify(mTelephonyManager, times(3))
+ .registerTelephonyCallback(any(), any(VcnUserMobileDataStateListener.class));
+ }
+
+ @Test
public void testMobileDataStateCheckedOnInitialization_enabled() {
// Validate state from setUp()
assertTrue(mVcn.isMobileDataEnabled());
@@ -263,6 +271,24 @@ public class VcnTest {
assertFalse(mVcn.isMobileDataEnabled());
}
+ @Test
+ public void testSubscriptionSnapshotUpdatesMobileDataStateListeners() {
+ final TelephonySubscriptionSnapshot updatedSnapshot =
+ mock(TelephonySubscriptionSnapshot.class);
+
+ doReturn(new ArraySet<>(Arrays.asList(2, 4)))
+ .when(updatedSnapshot)
+ .getAllSubIdsInGroup(any());
+
+ mVcn.updateSubscriptionSnapshot(updatedSnapshot);
+ mTestLooper.dispatchAll();
+
+ verify(mTelephonyManager, times(4))
+ .registerTelephonyCallback(any(), any(VcnUserMobileDataStateListener.class));
+ verify(mTelephonyManager, times(2))
+ .unregisterTelephonyCallback(any(VcnUserMobileDataStateListener.class));
+ }
+
private void triggerVcnRequestListeners(NetworkRequestListener requestListener) {
for (final int[] caps : TEST_CAPS) {
startVcnGatewayWithCapabilities(requestListener, caps);
@@ -402,24 +428,17 @@ public class VcnTest {
verify(mVcnNetworkProvider).resendAllRequests(requestListener);
}
- private void verifyMobileDataToggled(boolean startingToggleState, boolean endingToggleState) {
- final ArgumentCaptor<ContentObserver> captor =
- ArgumentCaptor.forClass(ContentObserver.class);
- verify(mContentResolver).registerContentObserver(any(), anyBoolean(), captor.capture());
- final ContentObserver contentObserver = captor.getValue();
-
+ private void setupForMobileDataTest(boolean startingToggleState) {
// Start VcnGatewayConnections
final NetworkRequestListener requestListener = verifyAndGetRequestListener();
mVcn.setMobileDataEnabled(startingToggleState);
triggerVcnRequestListeners(requestListener);
- final Map<VcnGatewayConnectionConfig, VcnGatewayConnection> gateways =
- mVcn.getVcnGatewayConnectionConfigMap();
-
- // Trigger data toggle change.
- doReturn(endingToggleState).when(mTelephonyManager).isDataEnabled();
- contentObserver.onChange(false /* selfChange, ignored */);
- mTestLooper.dispatchAll();
+ }
+ private void verifyMobileDataToggledUpdatesGatewayConnections(
+ boolean startingToggleState,
+ boolean endingToggleState,
+ Map<VcnGatewayConnectionConfig, VcnGatewayConnection> gateways) {
// Verify that data toggle changes restart ONLY INTERNET or DUN networks, and only if the
// toggle state changed.
for (Entry<VcnGatewayConnectionConfig, VcnGatewayConnection> entry : gateways.entrySet()) {
@@ -433,29 +452,98 @@ public class VcnTest {
}
}
+ final NetworkRequestListener requestListener = verifyAndGetRequestListener();
if (startingToggleState != endingToggleState) {
verify(mVcnNetworkProvider).resendAllRequests(requestListener);
}
assertEquals(endingToggleState, mVcn.isMobileDataEnabled());
}
+ private void verifyGlobalMobileDataToggled(
+ boolean startingToggleState, boolean endingToggleState) {
+ setupForMobileDataTest(startingToggleState);
+ final Map<VcnGatewayConnectionConfig, VcnGatewayConnection> gateways =
+ mVcn.getVcnGatewayConnectionConfigMap();
+
+ // Trigger data toggle change
+ final ArgumentCaptor<ContentObserver> captor =
+ ArgumentCaptor.forClass(ContentObserver.class);
+ verify(mContentResolver).registerContentObserver(any(), anyBoolean(), captor.capture());
+ final ContentObserver contentObserver = captor.getValue();
+
+ doReturn(endingToggleState).when(mTelephonyManager).isDataEnabled();
+ contentObserver.onChange(false /* selfChange, ignored */);
+ mTestLooper.dispatchAll();
+
+ // Verify resultant behavior
+ verifyMobileDataToggledUpdatesGatewayConnections(
+ startingToggleState, endingToggleState, gateways);
+ }
+
+ @Test
+ public void testGlobalMobileDataEnabled() {
+ verifyGlobalMobileDataToggled(
+ false /* startingToggleState */, true /* endingToggleState */);
+ }
+
+ @Test
+ public void testGlobalMobileDataDisabled() {
+ verifyGlobalMobileDataToggled(
+ true /* startingToggleState */, false /* endingToggleState */);
+ }
+
+ @Test
+ public void testGlobalMobileDataObserverFiredWithoutChanges_dataEnabled() {
+ verifyGlobalMobileDataToggled(
+ false /* startingToggleState */, false /* endingToggleState */);
+ }
+
+ @Test
+ public void testGlobalMobileDataObserverFiredWithoutChanges_dataDisabled() {
+ verifyGlobalMobileDataToggled(true /* startingToggleState */, true /* endingToggleState */);
+ }
+
+ private void verifySubscriptionMobileDataToggled(
+ boolean startingToggleState, boolean endingToggleState) {
+ setupForMobileDataTest(startingToggleState);
+ final Map<VcnGatewayConnectionConfig, VcnGatewayConnection> gateways =
+ mVcn.getVcnGatewayConnectionConfigMap();
+
+ // Trigger data toggle change.
+ final ArgumentCaptor<VcnUserMobileDataStateListener> captor =
+ ArgumentCaptor.forClass(VcnUserMobileDataStateListener.class);
+ verify(mTelephonyManager, times(3)).registerTelephonyCallback(any(), captor.capture());
+ final VcnUserMobileDataStateListener listener = captor.getValue();
+
+ doReturn(endingToggleState).when(mTelephonyManager).isDataEnabled();
+ listener.onUserMobileDataStateChanged(false /* enabled, ignored */);
+ mTestLooper.dispatchAll();
+
+ // Verify resultant behavior
+ verifyMobileDataToggledUpdatesGatewayConnections(
+ startingToggleState, endingToggleState, gateways);
+ }
+
@Test
- public void testMobileDataEnabled() {
- verifyMobileDataToggled(false /* startingToggleState */, true /* endingToggleState */);
+ public void testSubscriptionMobileDataEnabled() {
+ verifyGlobalMobileDataToggled(
+ false /* startingToggleState */, true /* endingToggleState */);
}
@Test
- public void testMobileDataDisabled() {
- verifyMobileDataToggled(true /* startingToggleState */, false /* endingToggleState */);
+ public void testSubscriptionMobileDataDisabled() {
+ verifyGlobalMobileDataToggled(
+ true /* startingToggleState */, false /* endingToggleState */);
}
@Test
- public void testMobileDataObserverFiredWithoutChanges_dataEnabled() {
- verifyMobileDataToggled(false /* startingToggleState */, false /* endingToggleState */);
+ public void testSubscriptionMobileDataListenerFiredWithoutChanges_dataEnabled() {
+ verifyGlobalMobileDataToggled(
+ false /* startingToggleState */, false /* endingToggleState */);
}
@Test
- public void testMobileDataObserverFiredWithoutChanges_dataDisabled() {
- verifyMobileDataToggled(true /* startingToggleState */, true /* endingToggleState */);
+ public void testSubscriptionMobileDataListenerFiredWithoutChanges_dataDisabled() {
+ verifyGlobalMobileDataToggled(true /* startingToggleState */, true /* endingToggleState */);
}
}
diff --git a/tools/fonts/fontchain_linter.py b/tools/fonts/fontchain_linter.py
index 3131f5687c6a..99f77fee4698 100755
--- a/tools/fonts/fontchain_linter.py
+++ b/tools/fonts/fontchain_linter.py
@@ -54,6 +54,7 @@ LANG_TO_SCRIPT = {
'or': 'Orya',
'pa': 'Guru',
'pt': 'Latn',
+ 'ru': 'Latn',
'sk': 'Latn',
'sl': 'Latn',
'sq': 'Latn',
diff --git a/tools/lint/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt b/tools/lint/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt
index 900c2145975a..a6fd9bba6192 100644
--- a/tools/lint/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt
+++ b/tools/lint/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt
@@ -31,7 +31,9 @@ class AndroidFrameworkIssueRegistry : IssueRegistry() {
CallingIdentityTokenDetector.ISSUE_RESTORE_IDENTITY_CALL_NOT_IN_FINALLY_BLOCK,
CallingIdentityTokenDetector.ISSUE_USE_OF_CALLER_AWARE_METHODS_WITH_CLEARED_IDENTITY,
CallingIdentityTokenDetector.ISSUE_CLEAR_IDENTITY_CALL_NOT_FOLLOWED_BY_TRY_FINALLY,
- CallingSettingsNonUserGetterMethodsDetector.ISSUE_NON_USER_GETTER_CALLED
+ CallingSettingsNonUserGetterMethodsDetector.ISSUE_NON_USER_GETTER_CALLED,
+ EnforcePermissionDetector.ISSUE_MISSING_ENFORCE_PERMISSION,
+ EnforcePermissionDetector.ISSUE_MISMATCHING_ENFORCE_PERMISSION
)
override val api: Int
diff --git a/tools/lint/checks/src/main/java/com/google/android/lint/EnforcePermissionDetector.kt b/tools/lint/checks/src/main/java/com/google/android/lint/EnforcePermissionDetector.kt
new file mode 100644
index 000000000000..8011b36c9a8f
--- /dev/null
+++ b/tools/lint/checks/src/main/java/com/google/android/lint/EnforcePermissionDetector.kt
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.lint
+
+import com.android.tools.lint.detector.api.AnnotationInfo
+import com.android.tools.lint.detector.api.AnnotationOrigin
+import com.android.tools.lint.detector.api.AnnotationUsageInfo
+import com.android.tools.lint.detector.api.AnnotationUsageType
+import com.android.tools.lint.detector.api.ConstantEvaluator
+import com.android.tools.lint.detector.api.Category
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Implementation
+import com.android.tools.lint.detector.api.Issue
+import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.Scope
+import com.android.tools.lint.detector.api.Severity
+import com.android.tools.lint.detector.api.SourceCodeScanner
+import com.intellij.psi.PsiAnnotation
+import com.intellij.psi.PsiClass
+import com.intellij.psi.PsiMethod
+import org.jetbrains.uast.UElement
+
+/**
+ * Lint Detector that ensures that any method overriding a method annotated
+ * with @EnforcePermission is also annotated with the exact same annotation.
+ * The intent is to surface the effective permission checks to the service
+ * implementations.
+ */
+class EnforcePermissionDetector : Detector(), SourceCodeScanner {
+
+ val ENFORCE_PERMISSION = "android.annotation.EnforcePermission"
+
+ override fun applicableAnnotations(): List<String> {
+ return listOf(ENFORCE_PERMISSION)
+ }
+
+ private fun areAnnotationsEquivalent(
+ context: JavaContext,
+ anno1: PsiAnnotation,
+ anno2: PsiAnnotation
+ ): Boolean {
+ if (anno1.qualifiedName != anno2.qualifiedName) {
+ return false
+ }
+ val attr1 = anno1.parameterList.attributes
+ val attr2 = anno2.parameterList.attributes
+ if (attr1.size != attr2.size) {
+ return false
+ }
+ for (i in attr1.indices) {
+ if (attr1[i].name != attr2[i].name) {
+ return false
+ }
+ val v1 = ConstantEvaluator.evaluate(context, attr1[i].value)
+ val v2 = ConstantEvaluator.evaluate(context, attr2[i].value)
+ if (v1 != v2) {
+ return false
+ }
+ }
+ return true
+ }
+
+ override fun visitAnnotationUsage(
+ context: JavaContext,
+ element: UElement,
+ annotationInfo: AnnotationInfo,
+ usageInfo: AnnotationUsageInfo
+ ) {
+ if (usageInfo.type == AnnotationUsageType.EXTENDS) {
+ val newClass = element.sourcePsi?.parent?.parent as PsiClass
+ val extendedClass: PsiClass = usageInfo.referenced as PsiClass
+ val newAnnotation = newClass.getAnnotation(ENFORCE_PERMISSION)
+ val extendedAnnotation = extendedClass.getAnnotation(ENFORCE_PERMISSION)!!
+
+ val location = context.getLocation(element)
+ val newClassName = newClass.qualifiedName
+ val extendedClassName = extendedClass.qualifiedName
+ if (newAnnotation == null) {
+ val msg = "The class $newClassName extends the class $extendedClassName which " +
+ "is annotated with @EnforcePermission. The same annotation must be used " +
+ "on $newClassName."
+ context.report(ISSUE_MISSING_ENFORCE_PERMISSION, element, location, msg)
+ } else if (!areAnnotationsEquivalent(context, newAnnotation, extendedAnnotation)) {
+ val msg = "The class $newClassName is annotated with ${newAnnotation.text} " +
+ "which differs from the parent class $extendedClassName: " +
+ "${extendedAnnotation.text}. The same annotation must be used for " +
+ "both classes."
+ context.report(ISSUE_MISMATCHING_ENFORCE_PERMISSION, element, location, msg)
+ }
+ } else if (usageInfo.type == AnnotationUsageType.METHOD_OVERRIDE &&
+ annotationInfo.origin == AnnotationOrigin.METHOD) {
+ val overridingMethod = element.sourcePsi as PsiMethod
+ val overriddenMethod = usageInfo.referenced as PsiMethod
+ val overridingAnnotation = overridingMethod.getAnnotation(ENFORCE_PERMISSION)
+ val overriddenAnnotation = overriddenMethod.getAnnotation(ENFORCE_PERMISSION)!!
+
+ val location = context.getLocation(element)
+ val overridingClass = overridingMethod.parent as PsiClass
+ val overriddenClass = overriddenMethod.parent as PsiClass
+ val overridingName = "${overridingClass.name}.${overridingMethod.name}"
+ val overriddenName = "${overriddenClass.name}.${overriddenMethod.name}"
+ if (overridingAnnotation == null) {
+ val msg = "The method $overridingName overrides the method $overriddenName which " +
+ "is annotated with @EnforcePermission. The same annotation must be used " +
+ "on $overridingName"
+ context.report(ISSUE_MISSING_ENFORCE_PERMISSION, element, location, msg)
+ } else if (!areAnnotationsEquivalent(
+ context, overridingAnnotation, overriddenAnnotation)) {
+ val msg = "The method $overridingName is annotated with " +
+ "${overridingAnnotation.text} which differs from the overridden " +
+ "method $overriddenName: ${overriddenAnnotation.text}. The same " +
+ "annotation must be used for both methods."
+ context.report(ISSUE_MISMATCHING_ENFORCE_PERMISSION, element, location, msg)
+ }
+ }
+ }
+
+ companion object {
+ val EXPLANATION = """
+ The @EnforcePermission annotation is used to indicate that the underlying binder code
+ has already verified the caller's permissions before calling the appropriate method. The
+ verification code is usually generated by the AIDL compiler, which also takes care of
+ annotating the generated Java code.
+
+ In order to surface that information to platform developers, the same annotation must be
+ used on the implementation class or methods.
+ """
+
+ val ISSUE_MISSING_ENFORCE_PERMISSION: Issue = Issue.create(
+ id = "MissingEnforcePermissionAnnotation",
+ briefDescription = "Missing @EnforcePermission annotation on Binder method",
+ explanation = EXPLANATION,
+ category = Category.SECURITY,
+ priority = 6,
+ severity = Severity.ERROR,
+ implementation = Implementation(
+ EnforcePermissionDetector::class.java,
+ Scope.JAVA_FILE_SCOPE
+ )
+ )
+
+ val ISSUE_MISMATCHING_ENFORCE_PERMISSION: Issue = Issue.create(
+ id = "MismatchingEnforcePermissionAnnotation",
+ briefDescription = "Incorrect @EnforcePermission annotation on Binder method",
+ explanation = EXPLANATION,
+ category = Category.SECURITY,
+ priority = 6,
+ severity = Severity.ERROR,
+ implementation = Implementation(
+ EnforcePermissionDetector::class.java,
+ Scope.JAVA_FILE_SCOPE
+ )
+ )
+ }
+}
diff --git a/tools/lint/checks/src/test/java/com/google/android/lint/EnforcePermissionDetectorTest.kt b/tools/lint/checks/src/test/java/com/google/android/lint/EnforcePermissionDetectorTest.kt
new file mode 100644
index 000000000000..f5f4ebee24e0
--- /dev/null
+++ b/tools/lint/checks/src/test/java/com/google/android/lint/EnforcePermissionDetectorTest.kt
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.lint
+
+import com.android.tools.lint.checks.infrastructure.LintDetectorTest
+import com.android.tools.lint.checks.infrastructure.TestFile
+import com.android.tools.lint.checks.infrastructure.TestLintTask
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Issue
+
+@Suppress("UnstableApiUsage")
+class EnforcePermissionDetectorTest : LintDetectorTest() {
+ override fun getDetector(): Detector = EnforcePermissionDetector()
+
+ override fun getIssues(): List<Issue> = listOf(
+ EnforcePermissionDetector.ISSUE_MISSING_ENFORCE_PERMISSION,
+ EnforcePermissionDetector.ISSUE_MISMATCHING_ENFORCE_PERMISSION
+ )
+
+ override fun lint(): TestLintTask = super.lint().allowMissingSdk(true)
+
+ fun testDoesNotDetectIssuesCorrectAnnotationOnClass() {
+ lint().files(java(
+ """
+ package test.pkg;
+ @android.annotation.EnforcePermission(android.Manifest.permission.READ_PHONE_STATE)
+ public class TestClass1 extends IFoo.Stub {
+ public void testMethod() {}
+ }
+ """).indented(),
+ *stubs
+ )
+ .run()
+ .expectClean()
+ }
+
+ fun testDoesNotDetectIssuesCorrectAnnotationOnMethod() {
+ lint().files(java(
+ """
+ package test.pkg;
+ import android.annotation.EnforcePermission;
+ public class TestClass2 extends IFooMethod.Stub {
+ @Override
+ @EnforcePermission(android.Manifest.permission.READ_PHONE_STATE)
+ public void testMethod() {}
+ }
+ """).indented(),
+ *stubs
+ )
+ .run()
+ .expectClean()
+ }
+
+ fun testDetectIssuesMismatchingAnnotationOnClass() {
+ lint().files(java(
+ """
+ package test.pkg;
+ @android.annotation.EnforcePermission(android.Manifest.permission.INTERNET)
+ public class TestClass3 extends IFoo.Stub {
+ public void testMethod() {}
+ }
+ """).indented(),
+ *stubs
+ )
+ .run()
+ .expect("""src/test/pkg/TestClass3.java:3: Error: The class test.pkg.TestClass3 is \
+annotated with @android.annotation.EnforcePermission(android.Manifest.permission.INTERNET) \
+which differs from the parent class IFoo.Stub: \
+@android.annotation.EnforcePermission(android.Manifest.permission.READ_PHONE_STATE). The \
+same annotation must be used for both classes. [MismatchingEnforcePermissionAnnotation]
+public class TestClass3 extends IFoo.Stub {
+ ~~~~~~~~~
+1 errors, 0 warnings""".addLineContinuation())
+ }
+
+ fun testDetectIssuesMismatchingAnnotationOnMethod() {
+ lint().files(java(
+ """
+ package test.pkg;
+ public class TestClass4 extends IFooMethod.Stub {
+ @android.annotation.EnforcePermission(android.Manifest.permission.INTERNET)
+ public void testMethod() {}
+ }
+ """).indented(),
+ *stubs
+ )
+ .run()
+ .expect("""src/test/pkg/TestClass4.java:4: Error: The method TestClass4.testMethod is \
+annotated with @android.annotation.EnforcePermission(android.Manifest.permission.INTERNET) \
+which differs from the overridden method Stub.testMethod: \
+@android.annotation.EnforcePermission(android.Manifest.permission.READ_PHONE_STATE). The same \
+annotation must be used for both methods. [MismatchingEnforcePermissionAnnotation]
+ public void testMethod() {}
+ ~~~~~~~~~~
+1 errors, 0 warnings""".addLineContinuation())
+ }
+
+ fun testDetectIssuesMissingAnnotationOnClass() {
+ lint().files(java(
+ """
+ package test.pkg;
+ public class TestClass5 extends IFoo.Stub {
+ public void testMethod() {}
+ }
+ """).indented(),
+ *stubs
+ )
+ .run()
+ .expect("""src/test/pkg/TestClass5.java:2: Error: The class test.pkg.TestClass5 extends \
+the class IFoo.Stub which is annotated with @EnforcePermission. The same annotation must be \
+used on test.pkg.TestClass5. [MissingEnforcePermissionAnnotation]
+public class TestClass5 extends IFoo.Stub {
+ ~~~~~~~~~
+1 errors, 0 warnings""".addLineContinuation())
+ }
+
+ fun testDetectIssuesMissingAnnotationOnMethod() {
+ lint().files(java(
+ """
+ package test.pkg;
+ public class TestClass6 extends IFooMethod.Stub {
+ public void testMethod() {}
+ }
+ """).indented(),
+ *stubs
+ )
+ .run()
+ .expect("""src/test/pkg/TestClass6.java:3: Error: The method TestClass6.testMethod \
+overrides the method Stub.testMethod which is annotated with @EnforcePermission. The same \
+annotation must be used on TestClass6.testMethod [MissingEnforcePermissionAnnotation]
+ public void testMethod() {}
+ ~~~~~~~~~~
+1 errors, 0 warnings""".addLineContinuation())
+ }
+
+ /* Stubs */
+
+ private val interfaceIFooStub: TestFile = java(
+ """
+ @android.annotation.EnforcePermission(android.Manifest.permission.READ_PHONE_STATE)
+ public interface IFoo {
+ @android.annotation.EnforcePermission(android.Manifest.permission.READ_PHONE_STATE)
+ public static abstract class Stub implements IFoo {
+ @Override
+ public void testMethod() {}
+ }
+ public void testMethod();
+ }
+ """
+ ).indented()
+
+ private val interfaceIFooMethodStub: TestFile = java(
+ """
+ public interface IFooMethod {
+ public static abstract class Stub implements IFooMethod {
+ @Override
+ @android.annotation.EnforcePermission(android.Manifest.permission.READ_PHONE_STATE)
+ public void testMethod() {}
+ }
+ @android.annotation.EnforcePermission(android.Manifest.permission.READ_PHONE_STATE)
+ public void testMethod();
+ }
+ """
+ ).indented()
+
+ private val manifestPermissionStub: TestFile = java(
+ """
+ package android.Manifest;
+ class permission {
+ public static final String READ_PHONE_STATE = "android.permission.READ_PHONE_STATE";
+ public static final String INTERNET = "android.permission.INTERNET";
+ }
+ """
+ ).indented()
+
+ private val enforcePermissionAnnotationStub: TestFile = java(
+ """
+ package android.annotation;
+ public @interface EnforcePermission {}
+ """
+ ).indented()
+
+ private val stubs = arrayOf(interfaceIFooStub, interfaceIFooMethodStub,
+ manifestPermissionStub, enforcePermissionAnnotationStub)
+
+ // Substitutes "backslash + new line" with an empty string to imitate line continuation
+ private fun String.addLineContinuation(): String = this.trimIndent().replace("\\\n", "")
+}
diff --git a/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java b/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java
index 3b7566051fae..459696e81ee4 100644
--- a/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java
+++ b/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java
@@ -1262,6 +1262,26 @@ public class WifiNl80211Manager {
}
/**
+ * Notifies the wificond daemon that the WiFi framework has successfully updated the Country
+ * Code of the driver. The wificond daemon needs this notification if the device does not
+ * support the NL80211_CMD_REG_CHANGED (otherwise it will find out on its own). The wificond
+ * updates in internal state in response to this Country Code update.
+ *
+ * @return true on success, false otherwise.
+ */
+ public boolean notifyCountryCodeChanged() {
+ try {
+ if (mWificond != null) {
+ mWificond.notifyCountryCodeChanged();
+ return true;
+ }
+ } catch (RemoteException e1) {
+ Log.e(TAG, "Failed to notify country code changed due to remote exception");
+ }
+ return false;
+ }
+
+ /**
* Register the provided callback handler for SoftAp events. The interface must first be created
* using {@link #setupInterfaceForSoftApMode(String)}. The callback registration is valid until
* the interface is deleted using {@link #tearDownSoftApInterface(String)} (no deregistration
diff --git a/wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java b/wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java
index 3fb23013bdec..4032a7b0c75c 100644
--- a/wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java
@@ -1137,6 +1137,26 @@ public class WifiNl80211ManagerTest {
assertEquals(capaExpected, capaActual);
}
+ /**
+ * Tests notifyCountryCodeChanged
+ */
+ @Test
+ public void testNotifyCountryCodeChanged() throws Exception {
+ doNothing().when(mWificond).notifyCountryCodeChanged();
+ assertTrue(mWificondControl.notifyCountryCodeChanged());
+ verify(mWificond).notifyCountryCodeChanged();
+ }
+
+ /**
+ * Tests notifyCountryCodeChanged with RemoteException
+ */
+ @Test
+ public void testNotifyCountryCodeChangedRemoteException() throws Exception {
+ doThrow(new RemoteException()).when(mWificond).notifyCountryCodeChanged();
+ assertFalse(mWificondControl.notifyCountryCodeChanged());
+ verify(mWificond).notifyCountryCodeChanged();
+ }
+
// Create a ArgumentMatcher which captures a SingleScanSettings parameter and checks if it
// matches the provided frequency set and ssid set.
private class ScanMatcher implements ArgumentMatcher<SingleScanSettings> {